Merge pull request #237 from billhollings/master

 Vulkan extension enhancements.
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index c41a0dd..2f87edd 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -9,6 +9,10 @@
 /* Begin PBXBuildFile section */
 		A9096E5E1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; };
 		A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; };
+		A909F65F213B190700FCD6BE /* MVKExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A909F65A213B190600FCD6BE /* MVKExtensions.h */; };
+		A909F660213B190700FCD6BE /* MVKExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A909F65A213B190600FCD6BE /* MVKExtensions.h */; };
+		A909F661213B190700FCD6BE /* MVKExtensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A909F65E213B190700FCD6BE /* MVKExtensions.cpp */; };
+		A909F662213B190700FCD6BE /* MVKExtensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A909F65E213B190700FCD6BE /* MVKExtensions.cpp */; };
 		A90C8DEA1F45354D009CB32C /* MVKCommandEncodingPool.h in Headers */ = {isa = PBXBuildFile; fileRef = A90C8DE81F45354D009CB32C /* MVKCommandEncodingPool.h */; };
 		A90C8DEB1F45354D009CB32C /* MVKCommandEncodingPool.h in Headers */ = {isa = PBXBuildFile; fileRef = A90C8DE81F45354D009CB32C /* MVKCommandEncodingPool.h */; };
 		A90C8DEC1F45354D009CB32C /* MVKCommandEncodingPool.mm in Sources */ = {isa = PBXBuildFile; fileRef = A90C8DE91F45354D009CB32C /* MVKCommandEncodingPool.mm */; };
@@ -147,8 +151,8 @@
 		A98149521FB6A3F7005F00B4 /* MVKEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */; };
 		A98149531FB6A3F7005F00B4 /* MVKFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149441FB6A3F7005F00B4 /* MVKFoundation.h */; };
 		A98149541FB6A3F7005F00B4 /* MVKFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149441FB6A3F7005F00B4 /* MVKFoundation.h */; };
-		A98149551FB6A3F7005F00B4 /* MVKFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A98149451FB6A3F7005F00B4 /* MVKFoundation.mm */; };
-		A98149561FB6A3F7005F00B4 /* MVKFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A98149451FB6A3F7005F00B4 /* MVKFoundation.mm */; };
+		A98149551FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A98149451FB6A3F7005F00B4 /* MVKFoundation.cpp */; };
+		A98149561FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A98149451FB6A3F7005F00B4 /* MVKFoundation.cpp */; };
 		A98149571FB6A3F7005F00B4 /* MVKObjectPool.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149461FB6A3F7005F00B4 /* MVKObjectPool.h */; };
 		A98149581FB6A3F7005F00B4 /* MVKObjectPool.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149461FB6A3F7005F00B4 /* MVKObjectPool.h */; };
 		A981495D1FB6A3F7005F00B4 /* MVKWatermark.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149491FB6A3F7005F00B4 /* MVKWatermark.h */; };
@@ -256,6 +260,8 @@
 /* Begin PBXFileReference section */
 		A9096E5C1F81E16300DFBEA6 /* MVKCmdDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVKCmdDispatch.h; sourceTree = "<group>"; };
 		A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCmdDispatch.mm; sourceTree = "<group>"; };
+		A909F65A213B190600FCD6BE /* MVKExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKExtensions.h; sourceTree = "<group>"; };
+		A909F65E213B190700FCD6BE /* MVKExtensions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MVKExtensions.cpp; sourceTree = "<group>"; };
 		A90C8DE81F45354D009CB32C /* MVKCommandEncodingPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCommandEncodingPool.h; sourceTree = "<group>"; };
 		A90C8DE91F45354D009CB32C /* MVKCommandEncodingPool.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCommandEncodingPool.mm; sourceTree = "<group>"; };
 		A93E832E2121C5D3001FEBD4 /* MVKGPUCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKGPUCapture.h; sourceTree = "<group>"; };
@@ -325,7 +331,7 @@
 		A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKBaseObject.h; sourceTree = "<group>"; };
 		A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKEnvironment.h; sourceTree = "<group>"; };
 		A98149441FB6A3F7005F00B4 /* MVKFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKFoundation.h; sourceTree = "<group>"; };
-		A98149451FB6A3F7005F00B4 /* MVKFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKFoundation.mm; sourceTree = "<group>"; };
+		A98149451FB6A3F7005F00B4 /* MVKFoundation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MVKFoundation.cpp; sourceTree = "<group>"; };
 		A98149461FB6A3F7005F00B4 /* MVKObjectPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKObjectPool.h; sourceTree = "<group>"; };
 		A98149491FB6A3F7005F00B4 /* MVKWatermark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKWatermark.h; sourceTree = "<group>"; };
 		A981494A1FB6A3F7005F00B4 /* MVKWatermark.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKWatermark.mm; sourceTree = "<group>"; };
@@ -365,7 +371,7 @@
 				A94FB7651C7DFB4800632CA3 /* API */,
 				A94FB76B1C7DFB4800632CA3 /* Commands */,
 				A94FB77E1C7DFB4800632CA3 /* GPUObjects */,
-				A94FB79F1C7DFB4800632CA3 /* Loader */,
+				A94FB79F1C7DFB4800632CA3 /* Layers */,
 				A9E53DCC2100B197002781DD /* OS */,
 				A98149401FB6A3F7005F00B4 /* Utility */,
 				A94FB7A81C7DFB4800632CA3 /* Vulkan */,
@@ -457,13 +463,15 @@
 			path = GPUObjects;
 			sourceTree = "<group>";
 		};
-		A94FB79F1C7DFB4800632CA3 /* Loader */ = {
+		A94FB79F1C7DFB4800632CA3 /* Layers */ = {
 			isa = PBXGroup;
 			children = (
+				A909F65A213B190600FCD6BE /* MVKExtensions.h */,
+				A909F65E213B190700FCD6BE /* MVKExtensions.cpp */,
 				A94FB7A01C7DFB4800632CA3 /* MVKLayers.h */,
 				A94FB7A11C7DFB4800632CA3 /* MVKLayers.mm */,
 			);
-			path = Loader;
+			path = Layers;
 			sourceTree = "<group>";
 		};
 		A94FB7A81C7DFB4800632CA3 /* Vulkan */ = {
@@ -483,7 +491,7 @@
 				A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */,
 				A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */,
 				A98149441FB6A3F7005F00B4 /* MVKFoundation.h */,
-				A98149451FB6A3F7005F00B4 /* MVKFoundation.mm */,
+				A98149451FB6A3F7005F00B4 /* MVKFoundation.cpp */,
 				A98149461FB6A3F7005F00B4 /* MVKObjectPool.h */,
 				A98149491FB6A3F7005F00B4 /* MVKWatermark.h */,
 				A981494A1FB6A3F7005F00B4 /* MVKWatermark.mm */,
@@ -576,6 +584,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				A94FB7B41C7DFB4800632CA3 /* vk_mvk_moltenvk.h in Headers */,
+				A909F65F213B190700FCD6BE /* MVKExtensions.h in Headers */,
 				A94FB7B01C7DFB4800632CA3 /* mvk_datatypes.h in Headers */,
 				A98149511FB6A3F7005F00B4 /* MVKEnvironment.h in Headers */,
 				A948BB7F1E51642700DE59F2 /* mvk_vulkan.h in Headers */,
@@ -635,6 +644,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				A94FB7B51C7DFB4800632CA3 /* vk_mvk_moltenvk.h in Headers */,
+				A909F660213B190700FCD6BE /* MVKExtensions.h in Headers */,
 				A94FB7B11C7DFB4800632CA3 /* mvk_datatypes.h in Headers */,
 				A98149521FB6A3F7005F00B4 /* MVKEnvironment.h in Headers */,
 				A948BB801E51642700DE59F2 /* mvk_vulkan.h in Headers */,
@@ -866,7 +876,8 @@
 				A94FB7C61C7DFB4800632CA3 /* MVKCmdRenderPass.mm in Sources */,
 				A94FB7DE1C7DFB4800632CA3 /* MVKBuffer.mm in Sources */,
 				A94FB82A1C7DFB4800632CA3 /* mvk_datatypes.mm in Sources */,
-				A98149551FB6A3F7005F00B4 /* MVKFoundation.mm in Sources */,
+				A909F661213B190700FCD6BE /* MVKExtensions.cpp in Sources */,
+				A98149551FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */,
 				A94FB7E61C7DFB4800632CA3 /* MVKDevice.mm in Sources */,
 				A9E53DF52100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */,
 				A94FB7FA1C7DFB4800632CA3 /* MVKPipeline.mm in Sources */,
@@ -917,7 +928,8 @@
 				A94FB7C71C7DFB4800632CA3 /* MVKCmdRenderPass.mm in Sources */,
 				A94FB7DF1C7DFB4800632CA3 /* MVKBuffer.mm in Sources */,
 				A94FB82B1C7DFB4800632CA3 /* mvk_datatypes.mm in Sources */,
-				A98149561FB6A3F7005F00B4 /* MVKFoundation.mm in Sources */,
+				A909F662213B190700FCD6BE /* MVKExtensions.cpp in Sources */,
+				A98149561FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */,
 				A94FB7E71C7DFB4800632CA3 /* MVKDevice.mm in Sources */,
 				A9E53DF62100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */,
 				A94FB7FB1C7DFB4800632CA3 /* MVKPipeline.mm in Sources */,
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index c02676b..e95f1c6 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -31,7 +31,7 @@
 extern "C" {
 #endif	//  __cplusplus
 	
-#include <vulkan/vulkan.h>
+#include <MoltenVK/mvk_vulkan.h>
 
 #import <Metal/Metal.h>
 #import <CoreGraphics/CoreGraphics.h>
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 427a3b9..14067b3 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -26,7 +26,7 @@
 extern "C" {
 #endif	//  __cplusplus
 	
-#include <vulkan/vulkan.h>
+#include <MoltenVK/mvk_vulkan.h>
 
 #ifdef __OBJC__
 #import <Metal/Metal.h>
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index b28c2cd..b54fc06 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -62,6 +62,9 @@
     if (_srcImage->getMTLPixelFormat() != _dstImage->getMTLPixelFormat()) {
         setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): The source and destination images must have the same format."));
     }
+	if ((_srcImage->getMTLTextureType() == MTLTextureType3D) || (_dstImage->getMTLTextureType() == MTLTextureType3D)) {
+		setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdCopyImage(): Metal does not support copying to or from slices of a 3D texture."));
+	}
 }
 
 // Adds a Metal copy region structure for each layer in the specified copy region.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index d85e29c..f47f4ff 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -801,7 +801,11 @@
 												   const VkDescriptorSetLayout* pSetLayouts,
 												   VkDescriptorSet* pDescriptorSets) {
 	if (_allocatedSetCount + count > _maxSets) {
-		return mvkNotifyErrorWithText(VK_ERROR_OUT_OF_POOL_MEMORY_KHR, "The maximum number of descriptor sets that can be allocated by this descriptor pool is %d.", _maxSets);
+		if (_device->_enabledExtensions.vk_KHR_maintenance1.enabled) {
+			return VK_ERROR_OUT_OF_POOL_MEMORY;		// Failure is an acceptable test...don't log as error.
+		} else {
+			return mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "The maximum number of descriptor sets that can be allocated by this descriptor pool is %d.", _maxSets);
+		}
 	}
 
 	for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 4eb86f4..bf19fc8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -288,7 +288,7 @@
 public:
 
 	/** Returns a pointer to the Vulkan instance. */
-	inline MVKInstance* getInstance() { return _physicalDevice->getInstance(); }
+	inline MVKInstance* getInstance() { return _physicalDevice->_mvkInstance; }
 
 	/** Returns the physical device underlying this logical device. */
 	inline MVKPhysicalDevice* getPhysicalDevice() { return _physicalDevice; }
@@ -504,7 +504,7 @@
 
 #pragma mark Properties directly accessible
 
-	/** The MoltenVK configuration settings. */
+	/** Pointer to the MoltenVK configuration settings. */
 	const MVKConfiguration* _pMVKConfig;
 
 	/** Pointer to the feature set of the underlying physical device. */
@@ -519,6 +519,9 @@
 	/** Pointer to the memory properties of the underlying physical device. */
 	const VkPhysicalDeviceMemoryProperties* _pMemoryProperties;
 
+	/** The list of Vulkan extensions, indicating whether each has been enabled by the app for this device. */
+	const MVKExtensionList _enabledExtensions;
+
     /** Performance statistics. */
     MVKPerformanceStatistics _performanceStatistics;
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index ee5889c..905a95f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -1018,39 +1018,39 @@
 			break;
 	}
 
-	string fsMsg = "GPU device:";
-	fsMsg += "\n\t\tmodel: %s";
-	fsMsg += "\n\t\ttype: %s";
-	fsMsg += "\n\t\tvendorID: %#06x";
-	fsMsg += "\n\t\tdeviceID: %#06x";
-	fsMsg += "\n\t\tpipelineCacheUUID: %s";
-	fsMsg += "\n\tsupports the following Metal Feature Sets:";
+	string logMsg = "GPU device:";
+	logMsg += "\n\t\tmodel: %s";
+	logMsg += "\n\t\ttype: %s";
+	logMsg += "\n\t\tvendorID: %#06x";
+	logMsg += "\n\t\tdeviceID: %#06x";
+	logMsg += "\n\t\tpipelineCacheUUID: %s";
+	logMsg += "\n\tsupports the following Metal Feature Sets:";
 
 #if MVK_IOS
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily4_v1] ) { fsMsg += "\n\tviOS GPU Family 4 v1"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily4_v1] ) { logMsg += "\n\t\tiOS GPU Family 4 v1"; }
 
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v3] ) { fsMsg += "\n\t\tiOS GPU Family 3 v3"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v2] ) { fsMsg += "\n\t\tiOS GPU Family 3 v2"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { fsMsg += "\n\t\tiOS GPU Family 3 v1"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v3] ) { logMsg += "\n\t\tiOS GPU Family 3 v3"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v2] ) { logMsg += "\n\t\tiOS GPU Family 3 v2"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily3_v1] ) { logMsg += "\n\t\tiOS GPU Family 3 v1"; }
 
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v4] ) { fsMsg += "\n\t\tiOS GPU Family 2 v4"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v3] ) { fsMsg += "\n\t\tiOS GPU Family 2 v3"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v2] ) { fsMsg += "\n\t\tiOS GPU Family 2 v2"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1] ) { fsMsg += "\n\t\tiOS GPU Family 2 v1"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v4] ) { logMsg += "\n\t\tiOS GPU Family 2 v4"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v3] ) { logMsg += "\n\t\tiOS GPU Family 2 v3"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v2] ) { logMsg += "\n\t\tiOS GPU Family 2 v2"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily2_v1] ) { logMsg += "\n\t\tiOS GPU Family 2 v1"; }
 
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v4] ) { fsMsg += "\n\t\tiOS GPU Family 1 v4"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v3] ) { fsMsg += "\n\t\tiOS GPU Family 1 v3"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v2] ) { fsMsg += "\n\t\tiOS GPU Family 1 v2"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v1] ) { fsMsg += "\n\t\tiOS GPU Family 1 v1"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v4] ) { logMsg += "\n\t\tiOS GPU Family 1 v4"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v3] ) { logMsg += "\n\t\tiOS GPU Family 1 v3"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v2] ) { logMsg += "\n\t\tiOS GPU Family 1 v2"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v1] ) { logMsg += "\n\t\tiOS GPU Family 1 v1"; }
 #endif
 
 #if MVK_MACOS
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v3] ) { fsMsg += "\n\t\tmacOS GPU Family 1 v3"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2] ) { fsMsg += "\n\t\tmacOS GPU Family 1 v2"; }
-    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v1] ) { fsMsg += "\n\t\tmacOS GPU Family 1 v1"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v3] ) { logMsg += "\n\t\tmacOS GPU Family 1 v3"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2] ) { logMsg += "\n\t\tmacOS GPU Family 1 v2"; }
+    if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v1] ) { logMsg += "\n\t\tmacOS GPU Family 1 v1"; }
 #endif
 
-	MVKLogInfo(fsMsg.c_str(), _properties.deviceName, devTypeStr.c_str(), _properties.vendorID, _properties.deviceID,
+	MVKLogInfo(logMsg.c_str(), _properties.deviceName, devTypeStr.c_str(), _properties.vendorID, _properties.deviceID,
 			   [[[NSUUID alloc] initWithUUIDBytes: _properties.pipelineCacheUUID] autorelease].UUIDString.UTF8String);
 }
 
@@ -1523,7 +1523,7 @@
 	initPerformanceTracking();
 
 	_physicalDevice = physicalDevice;
-	_pMVKConfig = &_physicalDevice->getInstance()->_mvkConfig;
+	_pMVKConfig = _physicalDevice->_mvkInstance->getMoltenVKConfiguration();
 	_pFeatures = &_physicalDevice->_features;
 	_pMetalFeatures = &_physicalDevice->_metalFeatures;
 	_pProperties = &_physicalDevice->_properties;
@@ -1532,11 +1532,13 @@
     _globalVisibilityResultMTLBuffer = nil;
     _globalVisibilityQueryCount = 0;
 
-    // Verify the requested extension names. Should be same as those requested from instance.
-    setConfigurationResult(_physicalDevice->_mvkInstance->verifyExtensions(pCreateInfo->enabledExtensionCount,
-                                                                           pCreateInfo->ppEnabledExtensionNames));
+	_commandResourceFactory = new MVKCommandResourceFactory(this);
 
-    _commandResourceFactory = new MVKCommandResourceFactory(this);
+    // Verify the requested extension names. Should be same as those requested from instance.
+	MVKExtensionList* pWritableExtns = (MVKExtensionList*)&_enabledExtensions;
+	setConfigurationResult(pWritableExtns->enable(pCreateInfo->enabledExtensionCount,
+												  pCreateInfo->ppEnabledExtensionNames,
+												  getInstance()->getDriverLayer()->getSupportedExtensions()));
 
 	// Create the queues
 	uint32_t qrCnt = pCreateInfo->queueCreateInfoCount;
@@ -1551,7 +1553,9 @@
 		}
 	}
 
-	MVKLogInfo("Created VkDevice to run on GPU %s", _pProperties->deviceName);
+	string logMsg = "Created VkDevice to run on GPU %s with the following Vulkan extensions enabled:";
+	logMsg += _enabledExtensions.enabledNamesString("\n\t\t", true);
+	MVKLogInfo(logMsg.c_str(), _pProperties->deviceName);
 }
 
 void MVKDevice::initPerformanceTracking() {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 087b5f2..48e0e53 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -281,6 +281,7 @@
     bool matchesSwizzle(VkComponentMapping components, VkComponentMapping pattern);
     const char* getSwizzleName(VkComponentSwizzle swizzle);
     void setSwizzleFormatError(VkFormat format, VkComponentMapping components);
+	void validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo);
 
     MVKImage* _image;
     VkImageSubresourceRange _subresourceRange;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 6d27cfd..6b84a1b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -614,6 +614,8 @@
 
 MVKImageView::MVKImageView(MVKDevice* device, const VkImageViewCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) {
 
+	validateImageViewConfig(pCreateInfo);
+
 	_image = (MVKImage*)pCreateInfo->image;
 
 	// Remember the subresource range, and determine the actual number of mip levels and texture slices
@@ -631,6 +633,17 @@
 	initMTLTextureViewSupport();
 }
 
+// Validate whether the image view configuration can be supported
+void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo) {
+	VkImageType imgType = ((MVKImage*)pCreateInfo->image)->getImageType();
+	VkImageViewType viewType = pCreateInfo->viewType;
+
+	// VK_KHR_maintenance1 supports taking 2D image views of 3D slices. No dice in Metal.
+	if ((viewType == VK_IMAGE_VIEW_TYPE_2D || viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (imgType == VK_IMAGE_TYPE_3D)) {
+		setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): Metal does not support creating a 2D view on a 3D image."));
+	}
+}
+
 // Returns a MTLPixelFormat, based on the original MTLPixelFormat, as converted from the VkFormat,
 // but possibly modified by the swizzles defined in the VkComponentMapping of the VkImageViewCreateInfo.
 // Metal does not support general per-texture swizzles, and so this function relies on a few coincidental
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
index e0222db..9c6fdc2 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include "MVKLayers.h"
 #include "MVKSurface.h"
 #include "MVKBaseObject.h"
 #include "vk_mvk_moltenvk.h"
@@ -53,17 +54,8 @@
 	 */
 	VkResult getPhysicalDevices(uint32_t* pCount, VkPhysicalDevice* pPhysicalDevices);
 
-    /** 
-     * Verifies that the list of layers are available, 
-     * and returns VK_SUCCESS or VK_ERROR_LAYER_NOT_PRESENT.
-     */
-    VkResult verifyLayers(uint32_t count, const char* const* names);
-
-    /**
-     * Verifies that the list of extensions are available,
-     * and returns VK_SUCCESS or VK_ERROR_EXTENSION_NOT_PRESENT.
-     */
-    VkResult verifyExtensions(uint32_t count, const char* const* names);
+	/** Returns the driver layer. */
+	MVKLayer* getDriverLayer() { return MVKLayerManager::globalManager()->getDriverLayer(); }
 
 	/** Creates and returns a new object. */
 	MVKSurface* createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
@@ -73,8 +65,14 @@
 	void destroySurface(MVKSurface* mvkSrfc,
 						const VkAllocationCallbacks* pAllocator);
 
-	/** The MoltenVK configuration settings. */
-	MVKConfiguration _mvkConfig;
+	/** Returns the MoltenVK configuration settings. */
+	const MVKConfiguration* getMoltenVKConfiguration() { return &_mvkConfig; }
+
+	/** Returns the MoltenVK configuration settings. */
+	void setMoltenVKConfiguration(MVKConfiguration* mvkConfig) { _mvkConfig = *mvkConfig; }
+
+	/** The list of Vulkan extensions, indicating whether each has been enabled by the app. */
+	const MVKExtensionList _enabledExtensions;
 
 
 #pragma mark Object Creation
@@ -102,7 +100,9 @@
 	void initProcAddrs();
 	void initConfig();
     void logVersions();
+	VkResult verifyLayers(uint32_t count, const char* const* names);
 
+	MVKConfiguration _mvkConfig;
 	VkApplicationInfo _appInfo;
 	std::vector<MVKPhysicalDevice*> _physicalDevices;
 	std::unordered_map<std::string, PFN_vkVoidFunction> _procAddrMap;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index 61df8ed..c9761c0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -18,7 +18,6 @@
 
 
 #include "MVKInstance.h"
-#include "MVKLayers.h"
 #include "MVKDevice.h"
 #include "MVKFoundation.h"
 #include "MVKEnvironment.h"
@@ -53,27 +52,6 @@
 	return result;
 }
 
-VkResult MVKInstance::verifyLayers(uint32_t count, const char* const* names) {
-    VkResult result = VK_SUCCESS;
-    for (uint32_t i = 0; i < count; i++) {
-        if ( !MVKLayerManager::globalManager()->getLayerNamed(names[i]) ) {
-            result = mvkNotifyErrorWithText(VK_ERROR_LAYER_NOT_PRESENT, "Vulkan layer %s is not supported.", names[i]);
-        }
-    }
-    return result;
-}
-
-VkResult MVKInstance::verifyExtensions(uint32_t count, const char* const* names) {
-    VkResult result = VK_SUCCESS;
-    MVKLayer* driverLayer = MVKLayerManager::globalManager()->getDriverLayer();
-    for (uint32_t i = 0; i < count; i++) {
-        if (!driverLayer->hasExtensionNamed(names[i])) {
-            result = mvkNotifyErrorWithText(VK_ERROR_EXTENSION_NOT_PRESENT, "Vulkan extension %s is not supported.", names[i]);
-        }
-    }
-    return result;
-}
-
 MVKSurface* MVKInstance::createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
 									   const VkAllocationCallbacks* pAllocator) {
 	return new MVKSurface(this, pCreateInfo, pAllocator);
@@ -87,6 +65,74 @@
 
 #pragma mark Object Creation
 
+// Returns an autoreleased array containing the MTLDevices available on this system,
+// sorted according to power, with higher power GPU's at the front of the array.
+// This ensures that a lazy app that simply grabs the first GPU will get a high-power one by default.
+// If the MVK_FORCE_LOW_POWER_GPU is defined, the returned array will only include low-power devices.
+static NSArray<id<MTLDevice>>* getAvailableMTLDevices() {
+#if MVK_MACOS
+	NSArray* mtlDevs = [MTLCopyAllDevices() autorelease];
+
+#ifdef MVK_FORCE_LOW_POWER_GPU
+	NSMutableArray* lpDevs = [[NSMutableArray new] autorelease];
+	for (id<MTLDevice> md in mtlDevs) {
+		if (md.isLowPower) { [lpDevs addObject: md]; }
+	}
+	return lpDevs;
+#else
+	return [mtlDevs sortedArrayUsingComparator: ^(id<MTLDevice> md1, id<MTLDevice> md2) {
+		BOOL md1IsLP = md1.isLowPower;
+		BOOL md2IsLP = md2.isLowPower;
+
+		if (md1IsLP == md2IsLP) {
+			// If one device is headless and the other one is not, select the
+			// one that is not headless first.
+			BOOL md1IsHeadless = md1.isHeadless;
+			BOOL md2IsHeadless = md2.isHeadless;
+			if (md1IsHeadless == md2IsHeadless ) {
+				return NSOrderedSame;
+			}
+			return md2IsHeadless ? NSOrderedAscending : NSOrderedDescending;
+		}
+
+		return md2IsLP ? NSOrderedAscending : NSOrderedDescending;
+	}];
+#endif	// MVK_MACOS
+
+#endif
+#if MVK_IOS
+	return [NSArray arrayWithObject: MTLCreateSystemDefaultDevice()];
+#endif
+}
+
+MVKInstance::MVKInstance(const VkInstanceCreateInfo* pCreateInfo) {
+
+	_appInfo.apiVersion = MVK_VULKAN_API_VERSION;	// Default
+	mvkSetOrClear(&_appInfo, pCreateInfo->pApplicationInfo);
+
+	initProcAddrs();		// Init function pointers
+	initConfig();
+
+	setConfigurationResult(verifyLayers(pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames));
+	MVKExtensionList* pWritableExtns = (MVKExtensionList*)&_enabledExtensions;
+	setConfigurationResult(pWritableExtns->enable(pCreateInfo->enabledExtensionCount,
+												  pCreateInfo->ppEnabledExtensionNames,
+												  getDriverLayer()->getSupportedExtensions()));
+	logVersions();	// Log the MoltenVK and Vulkan versions
+
+	if (MVK_VULKAN_API_VERSION_CONFORM(MVK_VULKAN_API_VERSION) <
+		MVK_VULKAN_API_VERSION_CONFORM(_appInfo.apiVersion)) {
+		setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INCOMPATIBLE_DRIVER, "Request for driver version %x is not compatible with provided version %x.", _appInfo.apiVersion, MVK_VULKAN_API_VERSION));
+	}
+
+	// Populate the array of physical GPU devices
+	NSArray<id<MTLDevice>>* mtlDevices = getAvailableMTLDevices();
+	_physicalDevices.reserve(mtlDevices.count);
+	for (id<MTLDevice> mtlDev in mtlDevices) {
+		_physicalDevices.push_back(new MVKPhysicalDevice(this, mtlDev));
+	}
+}
+
 #define ADD_PROC_ADDR(entrypoint)	_procAddrMap[""#entrypoint] = (PFN_vkVoidFunction)&entrypoint;
 
 /** Initializes the function pointer map. */
@@ -276,7 +322,6 @@
 	ADD_PROC_ADDR(vkSetMoltenVKDeviceConfigurationMVK);
 #pragma clang diagnostic pop
 
-
 }
 
 void MVKInstance::logVersions() {
@@ -284,74 +329,14 @@
     char mvkVer[buffLen];
     char vkVer[buffLen];
     vkGetVersionStringsMVK(mvkVer, buffLen, vkVer, buffLen);
-    MVKLogInfo("MoltenVK version %s. Vulkan version %s.", mvkVer, vkVer);
-}
 
-/**
- * Returns an autoreleased array containing the MTLDevices available on this system,
- * sorted according to power, with higher power GPU's at the front of the array.
- * This ensures that a lazy app that simply grabs the first GPU will get a high-power one by default.
- * If the MVK_FORCE_LOW_POWER_GPU is defined, the returned array will only include low-power devices.
- */
-static NSArray<id<MTLDevice>>* getAvailableMTLDevices() {
-#if MVK_MACOS
-	NSArray* mtlDevs = [MTLCopyAllDevices() autorelease];
-
-#ifdef MVK_FORCE_LOW_POWER_GPU
-	NSMutableArray* lpDevs = [[NSMutableArray new] autorelease];
-	for (id<MTLDevice> md in mtlDevs) {
-		if (md.isLowPower) { [lpDevs addObject: md]; }
-	}
-	return lpDevs;
-#else
-	return [mtlDevs sortedArrayUsingComparator: ^(id<MTLDevice> md1, id<MTLDevice> md2) {
-		BOOL md1IsLP = md1.isLowPower;
-		BOOL md2IsLP = md2.isLowPower;
-
-		if (md1IsLP == md2IsLP) {
-			// If one device is headless and the other one is not, select the
-			// one that is not headless first.
-			BOOL md1IsHeadless = md1.isHeadless;
-			BOOL md2IsHeadless = md2.isHeadless;
-			if (md1IsHeadless == md2IsHeadless ) {
-				return NSOrderedSame;
-			}
-			return md2IsHeadless ? NSOrderedAscending : NSOrderedDescending;
-		}
-
-		return md2IsLP ? NSOrderedAscending : NSOrderedDescending;
-	}];
-#endif	// MVK_MACOS
-
-#endif
-#if MVK_IOS
-	return [NSArray arrayWithObject: MTLCreateSystemDefaultDevice()];
-#endif
-}
-
-MVKInstance::MVKInstance(const VkInstanceCreateInfo* pCreateInfo) {
-
-	_appInfo.apiVersion = MVK_VULKAN_API_VERSION;	// Default
-	mvkSetOrClear(&_appInfo, pCreateInfo->pApplicationInfo);
-
-    logVersions();          // Log the MoltenVK and Vulkan versions
-	initProcAddrs();		// Init function pointers
-	initConfig();
-
-	// Populate the array of physical GPU devices
-	NSArray<id<MTLDevice>>* mtlDevices = getAvailableMTLDevices();
-	_physicalDevices.reserve(mtlDevices.count);
-	for (id<MTLDevice> mtlDev in mtlDevices) {
-		_physicalDevices.push_back(new MVKPhysicalDevice(this, mtlDev));
-	}
-
-	if (MVK_VULKAN_API_VERSION_CONFORM(MVK_VULKAN_API_VERSION) <
-		MVK_VULKAN_API_VERSION_CONFORM(_appInfo.apiVersion)) {
-        setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INCOMPATIBLE_DRIVER, "Request for driver version %x is not compatible with provided version %x.", _appInfo.apiVersion, MVK_VULKAN_API_VERSION));
-	}
-
-    setConfigurationResult(verifyLayers(pCreateInfo->enabledLayerCount, pCreateInfo->ppEnabledLayerNames));
-    setConfigurationResult(verifyExtensions(pCreateInfo->enabledExtensionCount, pCreateInfo->ppEnabledExtensionNames));
+	const char* indent = "\n\t\t";
+	string logMsg = "MoltenVK version %s. Vulkan version %s.";
+	logMsg += "\n\tThe following Vulkan extensions are supported:";
+	logMsg += getDriverLayer()->getSupportedExtensions()->enabledNamesString(indent, true);
+	logMsg += "\n\tCreated VkInstance with the following Vulkan extensions enabled:";
+	logMsg += _enabledExtensions.enabledNamesString(indent, true);
+	MVKLogInfo(logMsg.c_str(), mvkVer, vkVer);
 }
 
 // Init config.
@@ -368,6 +353,16 @@
 	_mvkConfig.metalCompileTimeout			= MVK_CONFIG_METAL_COMPILE_TIMEOUT;
 }
 
+VkResult MVKInstance::verifyLayers(uint32_t count, const char* const* names) {
+    VkResult result = VK_SUCCESS;
+    for (uint32_t i = 0; i < count; i++) {
+        if ( !MVKLayerManager::globalManager()->getLayerNamed(names[i]) ) {
+            result = mvkNotifyErrorWithText(VK_ERROR_LAYER_NOT_PRESENT, "Vulkan layer %s is not supported.", names[i]);
+        }
+    }
+    return result;
+}
+
 MVKInstance::~MVKInstance() {
 	mvkDestroyContainerContents(_physicalDevices);
 }
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.cpp b/MoltenVK/MoltenVK/Layers/MVKExtensions.cpp
new file mode 100644
index 0000000..f66b76a
--- /dev/null
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.cpp
@@ -0,0 +1,149 @@
+/*
+ * MVKExtensions.cpp
+ *
+ * Copyright (c) 2014-2018 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.
+ */
+
+#include "MVKExtensions.h"
+#include "MVKFoundation.h"
+#include "vk_mvk_moltenvk.h"
+#include <vulkan/vulkan_ios.h>
+#include <vulkan/vulkan_macos.h>
+
+using namespace std;
+
+
+// Returns a VkExtensionProperties struct populated with a name and version
+static VkExtensionProperties mvkMakeExtProps(const char* extensionName, uint32_t specVersion) {
+	VkExtensionProperties extProps;
+	memset(extProps.extensionName, 0, sizeof(extProps.extensionName));
+	if (extensionName) { strcpy(extProps.extensionName, extensionName); }
+	extProps.specVersion = specVersion;
+	return extProps;
+}
+
+// Declares and populates a static VkExtensionProperties variable for the extention EXT,
+// which should include the unique portion of the extension name, as uppercase.
+// For example, for the extension VK_KHR_surface, use KHR_SURFACE.
+#define MVK_MAKE_VK_EXT_PROPS(EXT) \
+static VkExtensionProperties kVkExtProps_ ##EXT = mvkMakeExtProps(VK_ ##EXT ##_EXTENSION_NAME, VK_ ##EXT ##_SPEC_VERSION)
+
+// Extension properties
+MVK_MAKE_VK_EXT_PROPS(MVK_MOLTENVK);
+MVK_MAKE_VK_EXT_PROPS(MVK_MACOS_SURFACE);
+MVK_MAKE_VK_EXT_PROPS(MVK_IOS_SURFACE);
+MVK_MAKE_VK_EXT_PROPS(KHR_SURFACE);
+MVK_MAKE_VK_EXT_PROPS(KHR_SWAPCHAIN);
+MVK_MAKE_VK_EXT_PROPS(KHR_MAINTENANCE1);
+MVK_MAKE_VK_EXT_PROPS(IMG_FORMAT_PVRTC);
+MVK_MAKE_VK_EXT_PROPS(AMD_NEGATIVE_VIEWPORT_HEIGHT);
+MVK_MAKE_VK_EXT_PROPS(KHR_SHADER_DRAW_PARAMETERS);
+MVK_MAKE_VK_EXT_PROPS(KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2);
+MVK_MAKE_VK_EXT_PROPS(KHR_PUSH_DESCRIPTOR);
+
+// Calls the constructor for a MVKExtension member variable, using the member name and the
+// portion of the extension name, as uppercase, used in the MVK_MAKE_VK_EXT_PROPS() macro above.
+// For example, for the memeber variable vk_KHR_surface, use MVKExt_CONSTRUCT(vk_KHR_surface, KHR_SURFACE).
+#define MVKExt_CONSTRUCT(var, EXT)	var(&kVkExtProps_ ##EXT, enableForPlatform)
+
+MVKExtensionList::MVKExtensionList(bool enableForPlatform) :
+	MVKExt_CONSTRUCT(vk_MVK_moltenvk, MVK_MOLTENVK),
+	MVKExt_CONSTRUCT(vk_MVK_macos_surface, MVK_MACOS_SURFACE),
+	MVKExt_CONSTRUCT(vk_MVK_ios_surface, MVK_IOS_SURFACE),
+	MVKExt_CONSTRUCT(vk_KHR_surface, KHR_SURFACE),
+	MVKExt_CONSTRUCT(vk_KHR_swapchain, KHR_SWAPCHAIN),
+	MVKExt_CONSTRUCT(vk_KHR_maintenance1, KHR_MAINTENANCE1),
+	MVKExt_CONSTRUCT(vk_IMG_format_pvrtc, IMG_FORMAT_PVRTC),
+	MVKExt_CONSTRUCT(vk_AMD_negative_viewport_height, AMD_NEGATIVE_VIEWPORT_HEIGHT),
+	MVKExt_CONSTRUCT(vk_KHR_shader_draw_parameters, KHR_SHADER_DRAW_PARAMETERS),
+	MVKExt_CONSTRUCT(vk_KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2),
+	MVKExt_CONSTRUCT(vk_KHR_push_descriptor, KHR_PUSH_DESCRIPTOR)
+{}
+
+bool MVKExtensionList::isEnabled(const char* extnName) const {
+	uint32_t extnCnt = getCount();
+	const MVKExtension* extnAry = &extensionArray;
+	for (uint32_t extnIdx = 0; extnIdx < extnCnt; extnIdx++) {
+		const MVKExtension& extn = extnAry[extnIdx];
+		if ( strcmp(extn.pProperties->extensionName, extnName) == 0 ) {
+			return extn.enabled;
+		}
+	}
+	return false;
+}
+
+void MVKExtensionList::enable(const char* extnName) {
+	uint32_t extnCnt = getCount();
+	MVKExtension* extnAry = &extensionArray;
+	for (uint32_t extnIdx = 0; extnIdx < extnCnt; extnIdx++) {
+		MVKExtension& extn = extnAry[extnIdx];
+		if ( strcmp(extn.pProperties->extensionName, extnName) == 0 ) {
+			extn.enabled = true;
+			return;
+		}
+	}
+}
+
+VkResult MVKExtensionList::enable(uint32_t count, const char* const* names, MVKExtensionList* parent) {
+	VkResult result = VK_SUCCESS;
+	for (uint32_t i = 0; i < count; i++) {
+		auto extnName = names[i];
+		if (parent && !parent->isEnabled(extnName)) {
+			result = mvkNotifyErrorWithText(VK_ERROR_EXTENSION_NOT_PRESENT, "Vulkan extension %s is not supported.", extnName);
+		} else {
+			enable(extnName);
+		}
+	}
+	return result;
+}
+
+string MVKExtensionList::enabledNamesString(const char* separator, bool prefixFirstWithSeparator) const {
+	string logMsg;
+	bool isFirst = true;
+	uint32_t extnCnt = getCount();
+	const MVKExtension* extnAry = &extensionArray;
+	for (uint32_t extnIdx = 0; extnIdx < extnCnt; extnIdx++) {
+		const MVKExtension& extn = extnAry[extnIdx];
+		if (extn.enabled) {
+			if ( !isFirst || prefixFirstWithSeparator ) { logMsg += separator; }
+			logMsg += extn.pProperties->extensionName;
+			logMsg += " v";
+			logMsg += to_string(extn.pProperties->specVersion);
+			isFirst  = false;
+		}
+	}
+	return logMsg;
+}
+
+// Returns whether the specified properties are valid for this platform
+static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
+#if !(MVK_IOS)
+	if (pProperties == &kVkExtProps_MVK_IOS_SURFACE) { return false; }
+	if (pProperties == &kVkExtProps_IMG_FORMAT_PVRTC) { return false; }
+#endif
+#if !(MVK_MACOS)
+	if (pProperties == &kVkExtProps_MVK_MACOS_SURFACE) { return false; }
+#endif
+
+	if (pProperties == &kVkExtProps_AMD_NEGATIVE_VIEWPORT_HEIGHT) { return false; }
+
+	return true;
+}
+
+// Disable by default unless asked to enable for platform and the extension is valid for this platform
+MVKExtension::MVKExtension(VkExtensionProperties* pProperties, bool enableForPlatform) {
+	this->pProperties = pProperties;
+	this->enabled = enableForPlatform && mvkIsSupportedOnPlatform(pProperties);
+}
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.h b/MoltenVK/MoltenVK/Layers/MVKExtensions.h
new file mode 100644
index 0000000..1b2a2e2
--- /dev/null
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.h
@@ -0,0 +1,82 @@
+/*
+ * MVKExtensions.h
+ *
+ * Copyright (c) 2014-2018 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_vulkan.h"
+#include <string>
+
+/** Describes a Vulkan extension and whether or not it is enabled or supported. */
+struct MVKExtension {
+	bool enabled = false;
+	VkExtensionProperties* pProperties;
+
+	MVKExtension(VkExtensionProperties* pProperties, bool enableForPlatform = false);
+};
+
+/**
+ * A fixed list of the Vulkan extensions known to MoltenVK, with
+ * an indication of whether each extension is supported/enabled.
+ *
+ * To add support for a Vulkan extension, add a variable to this list.
+ */
+struct MVKExtensionList {
+	union {
+		struct {
+			MVKExtension vk_MVK_moltenvk;
+			MVKExtension vk_MVK_macos_surface;
+			MVKExtension vk_MVK_ios_surface;
+			MVKExtension vk_KHR_surface;
+			MVKExtension vk_KHR_swapchain;
+			MVKExtension vk_KHR_maintenance1;
+			MVKExtension vk_IMG_format_pvrtc;
+			MVKExtension vk_AMD_negative_viewport_height;
+			MVKExtension vk_KHR_shader_draw_parameters;
+			MVKExtension vk_KHR_get_physical_device_properties2;
+			MVKExtension vk_KHR_push_descriptor;
+		};
+		MVKExtension extensionArray;
+	};
+
+	/** Returns the total number of extensions that are tracked by this object. */
+	static uint32_t getCount() { return sizeof(MVKExtensionList) / sizeof(MVKExtension); }
+
+	/** Returns whether the named extension is enabled. */
+	bool isEnabled(const char* extnName) const;
+
+	/** Enables the named extension. */
+	void enable(const char* extnName);
+
+	/**
+	 * Enables the named extensions.
+	 *
+	 * If parent is non null, the extension must also be enabled in the parent in order
+	 * for it to be enabled here. If it is not enabled in the parent, an error is logged
+	 * and returned. Returns VK_SUCCESS if all requested extensions were able to be enabled.
+	 */
+	VkResult enable(uint32_t count, const char* const* names, MVKExtensionList* parent = nullptr);
+
+	/**
+	 * Returns a string containing the names of the enabled extensions, separated by the separator string.
+	 * If prefixFirstWithSeparator is true the separator will also appear before the first extension name.
+	 */
+	std::string enabledNamesString(const char* separator = " ", bool prefixFirstWithSeparator = false) const;
+
+	MVKExtensionList(bool enableForPlatform = false);
+};
+
diff --git a/MoltenVK/MoltenVK/Loader/MVKLayers.h b/MoltenVK/MoltenVK/Layers/MVKLayers.h
similarity index 90%
rename from MoltenVK/MoltenVK/Loader/MVKLayers.h
rename to MoltenVK/MoltenVK/Layers/MVKLayers.h
index 2fb56dd..f539a31 100644
--- a/MoltenVK/MoltenVK/Loader/MVKLayers.h
+++ b/MoltenVK/MoltenVK/Layers/MVKLayers.h
@@ -18,8 +18,8 @@
 
 #pragma once
 
-#include "mvk_vulkan.h"
 #include "MVKBaseObject.h"
+#include "MVKExtensions.h"
 #include <vector>
 
 
@@ -50,16 +50,16 @@
 	 */
 	VkResult getExtensionProperties(uint32_t* pCount, VkExtensionProperties* pProperties);
 
-    /** Returns whether this layer supports the specified extension. */
-    bool hasExtensionNamed(const char* extnName);
+	/** Returns the list of supported extensions. */
+	MVKExtensionList* getSupportedExtensions() { return &_supportedExtensions; }
 
 	/** Default constructor.  This represents the driver implementation. */
 	MVKLayer();
 
 protected:
 	VkLayerProperties _layerProperties;
-	std::vector<VkExtensionProperties> _extensions;
-	
+	MVKExtensionList _supportedExtensions;
+
 };
 
 
@@ -70,12 +70,11 @@
 
 public:
 
-	/** Returns a pointer to the driver layer. */
+	/** Returns the driver layer. */
 	MVKLayer* getDriverLayer();
 
 	/**
-	 * Returns a pointe to the layer with the specified name, 
-	 * or null if no layer was found with that name.
+	 * Returns the layer with the specified name, or null if no layer was found with that name.
 	 *
 	 * If pLayerName is null, returns the driver layer, which is
 	 * the same layer returned by the getDriverLayer() function.
diff --git a/MoltenVK/MoltenVK/Layers/MVKLayers.mm b/MoltenVK/MoltenVK/Layers/MVKLayers.mm
new file mode 100644
index 0000000..d1764cd
--- /dev/null
+++ b/MoltenVK/MoltenVK/Layers/MVKLayers.mm
@@ -0,0 +1,131 @@
+/*
+ * MVKLayers.mm
+ *
+ * Copyright (c) 2014-2018 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.
+ */
+
+#include "MVKLayers.h"
+#include "MVKEnvironment.h"
+#include "vk_mvk_moltenvk.h"
+#include <mutex>
+
+using namespace std;
+
+
+#pragma mark MVKLayer
+
+const char* MVKLayer::getName() { return (const char*)&_layerProperties.layerName; }
+
+VkLayerProperties* const MVKLayer::getLayerProperties() { return &_layerProperties; }
+
+VkResult MVKLayer::getExtensionProperties(uint32_t* pCount, VkExtensionProperties* pProperties) {
+
+	uint32_t enabledCnt = 0;
+
+	// Iterate extensions and handle those that are enabled. Count them,
+	// and if they are to be returned, and there is room, do so.
+	uint32_t extnCnt = _supportedExtensions.getCount();
+	MVKExtension* extnAry = &_supportedExtensions.extensionArray;
+	for (uint32_t extnIdx = 0; extnIdx < extnCnt; extnIdx++) {
+		if (extnAry[extnIdx].enabled) {
+			if (pProperties) {
+				if (enabledCnt < *pCount) {
+					pProperties[enabledCnt] = *(extnAry[extnIdx].pProperties);
+				} else {
+					return VK_INCOMPLETE;
+				}
+			}
+			enabledCnt++;
+		}
+	}
+
+	// Return the count of enabled extensions. This will either be a
+	// count of all enabled extensions, or a count of those returned.
+	*pCount = enabledCnt;
+	return VK_SUCCESS;
+}
+
+
+#pragma mark Object Creation
+
+MVKLayer::MVKLayer() : _supportedExtensions(true) {
+
+	// The core driver layer
+	memset(_layerProperties.layerName, 0, sizeof(_layerProperties.layerName));
+	strcpy(_layerProperties.layerName, "MoltenVK");
+	memset(_layerProperties.description, 0, sizeof(_layerProperties.description));
+	strcpy(_layerProperties.description, "MoltenVK driver layer");
+	_layerProperties.specVersion = MVK_VULKAN_API_VERSION;
+	_layerProperties.implementationVersion = MVK_VERSION;
+}
+
+
+#pragma mark MVKLayerManager
+
+MVKLayer* MVKLayerManager::getDriverLayer() { return &(_layers[0]); }
+
+MVKLayer* MVKLayerManager::getLayerNamed(const char* pLayerName) {
+
+	// If name is null, return the driver layer
+	if ( !pLayerName ) { return getDriverLayer(); }
+
+	// Otherwise look for a layer with the specified name
+	uint32_t layCnt = (uint32_t)_layers.size();
+	for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) {
+		MVKLayer* pLayer = &_layers[layIdx];
+		if ( strcmp(pLayer->getName(), pLayerName) == 0 ) { return pLayer; }
+	}
+	return VK_NULL_HANDLE;
+}
+
+VkResult MVKLayerManager::getLayerProperties(uint32_t* pCount, VkLayerProperties* pProperties) {
+
+	// If properties aren't actually being requested yet, simply update the returned count
+	if ( !pProperties ) {
+		*pCount = (uint32_t)_layers.size();
+		return VK_SUCCESS;
+	}
+
+	// Othewise, determine how many layers we'll return, and return that count
+	uint32_t layerCnt = (uint32_t)_layers.size();
+	VkResult result = (*pCount <= layerCnt) ? VK_SUCCESS : VK_INCOMPLETE;
+	*pCount = min(layerCnt, *pCount);
+
+	// Now populate the layer properties
+	for (uint32_t layIdx = 0; layIdx < *pCount; layIdx++) {
+		pProperties[layIdx] = *(&_layers[layIdx])->getLayerProperties();
+	}
+
+	return result;
+}
+
+
+#pragma mark Object Creation
+
+// Populate the layers
+MVKLayerManager::MVKLayerManager() {
+	_layers.push_back(MVKLayer());
+}
+
+static mutex _lock;
+static MVKLayerManager* _globalManager = VK_NULL_HANDLE;
+
+MVKLayerManager* MVKLayerManager::globalManager() {
+	lock_guard<mutex> lock(_lock);
+	if ( !_globalManager ) { _globalManager = new MVKLayerManager(); }
+	return _globalManager;
+}
+
+
diff --git a/MoltenVK/MoltenVK/Loader/MVKLayers.mm b/MoltenVK/MoltenVK/Loader/MVKLayers.mm
deleted file mode 100644
index 42f331e..0000000
--- a/MoltenVK/MoltenVK/Loader/MVKLayers.mm
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * MVKLayers.mm
- *
- * Copyright (c) 2014-2018 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.
- */
-
-#include "MVKLayers.h"
-#include "MVKEnvironment.h"
-#include "vk_mvk_moltenvk.h"
-#include <mutex>
-
-using namespace std;
-
-
-#pragma mark MVKLayer
-
-const char* MVKLayer::getName() { return (const char*)&_layerProperties.layerName; }
-
-VkLayerProperties* const MVKLayer::getLayerProperties() { return &_layerProperties; }
-
-VkResult MVKLayer::getExtensionProperties(uint32_t* pCount, VkExtensionProperties* pProperties) {
-
-	// If properties aren't actually being requested yet, simply update the returned count
-	if ( !pProperties ) {
-		*pCount = (uint32_t)_extensions.size();
-		return VK_SUCCESS;
-	}
-
-	// Othewise, determine how many extensions we'll return, and return that count
-	uint32_t extCnt = (uint32_t)_extensions.size();
-	VkResult result = (*pCount <= extCnt) ? VK_SUCCESS : VK_INCOMPLETE;
-	*pCount = min(extCnt, *pCount);
-
-	// Now populate the layer properties
-	for (uint32_t extIdx = 0; extIdx < *pCount; extIdx++) {
-		pProperties[extIdx] = _extensions[extIdx];
-	}
-
-	return result;
-}
-
-bool MVKLayer::hasExtensionNamed(const char* extnName) {
-    for (auto& extn : _extensions) {
-        if ( strcmp(extn.extensionName, extnName) == 0 ) { return true; }
-    }
-    return false;
-}
-
-
-#pragma mark Object Creation
-
-MVKLayer::MVKLayer() {
-
-	// The core driver layer
-	strcpy(_layerProperties.layerName, "MoltenVK");
-	strcpy(_layerProperties.description, "MoltenVK driver layer");
-	_layerProperties.specVersion = MVK_VULKAN_API_VERSION;
-	_layerProperties.implementationVersion = MVK_VERSION;
-
-    // Extensions
-    VkExtensionProperties extTmplt;
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_MVK_MOLTENVK_EXTENSION_NAME);
-    extTmplt.specVersion = VK_MVK_MOLTENVK_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-	strcpy(extTmplt.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
-    extTmplt.specVersion = VK_KHR_SWAPCHAIN_SPEC_VERSION;
-	_extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_KHR_SURFACE_EXTENSION_NAME);
-    extTmplt.specVersion = VK_KHR_SURFACE_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME);
-    extTmplt.specVersion = VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME);
-    extTmplt.specVersion = VK_KHR_MAINTENANCE1_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME);
-    extTmplt.specVersion = VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
-    extTmplt.specVersion = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
-    extTmplt.specVersion = VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-
-#if MVK_IOS
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-	strcpy(extTmplt.extensionName, VK_MVK_IOS_SURFACE_EXTENSION_NAME);
-    extTmplt.specVersion = VK_MVK_IOS_SURFACE_SPEC_VERSION;
-	_extensions.push_back(extTmplt);
-
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-    strcpy(extTmplt.extensionName, VK_IMG_FORMAT_PVRTC_EXTENSION_NAME);
-    extTmplt.specVersion = VK_IMG_FORMAT_PVRTC_SPEC_VERSION;
-    _extensions.push_back(extTmplt);
-#endif
-#if MVK_MACOS
-    memset(extTmplt.extensionName, 0, sizeof(extTmplt.extensionName));
-	strcpy(extTmplt.extensionName, VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
-    extTmplt.specVersion = VK_MVK_MACOS_SURFACE_SPEC_VERSION;
-	_extensions.push_back(extTmplt);
-#endif
-}
-
-
-#pragma mark MVKLayerManager
-
-MVKLayer* MVKLayerManager::getDriverLayer() { return &(_layers[0]); }
-
-MVKLayer* MVKLayerManager::getLayerNamed(const char* pLayerName) {
-
-	// If name is null, return the driver layer
-	if ( !pLayerName ) { return getDriverLayer(); }
-
-	// Otherwise look for a layer with the specified name
-	uint32_t layCnt = (uint32_t)_layers.size();
-	for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) {
-		MVKLayer* pLayer = &_layers[layIdx];
-		if ( strcmp(pLayer->getName(), pLayerName) == 0 ) { return pLayer; }
-	}
-	return VK_NULL_HANDLE;
-}
-
-
-VkResult MVKLayerManager::getLayerProperties(uint32_t* pCount, VkLayerProperties* pProperties) {
-
-	// If properties aren't actually being requested yet, simply update the returned count
-	if ( !pProperties ) {
-		*pCount = (uint32_t)_layers.size();
-		return VK_SUCCESS;
-	}
-
-	// Othewise, determine how many layers we'll return, and return that count
-	uint32_t layerCnt = (uint32_t)_layers.size();
-	VkResult result = (*pCount <= layerCnt) ? VK_SUCCESS : VK_INCOMPLETE;
-	*pCount = min(layerCnt, *pCount);
-
-	// Now populate the layer properties
-	for (uint32_t layIdx = 0; layIdx < *pCount; layIdx++) {
-		pProperties[layIdx] = *(&_layers[layIdx])->getLayerProperties();
-	}
-
-	return result;
-}
-
-
-#pragma mark Object Creation
-
-// Populate the layers
-MVKLayerManager::MVKLayerManager() {
-	_layers.push_back(MVKLayer());
-}
-
-static mutex _lock;
-static MVKLayerManager* _globalManager = VK_NULL_HANDLE;
-
-MVKLayerManager* MVKLayerManager::globalManager() {
-	lock_guard<mutex> lock(_lock);
-	if ( !_globalManager ) { _globalManager = new MVKLayerManager(); }
-	return _globalManager;
-}
-
-
diff --git a/MoltenVK/MoltenVK/OS/MVKOSExtensions.h b/MoltenVK/MoltenVK/OS/MVKOSExtensions.h
index 4c65590..f2e520b 100644
--- a/MoltenVK/MoltenVK/OS/MVKOSExtensions.h
+++ b/MoltenVK/MoltenVK/OS/MVKOSExtensions.h
@@ -18,7 +18,7 @@
 
 #pragma once
 
-#include <vulkan/vulkan.h>
+#include "mvk_vulkan.h"
 
 #import <Metal/Metal.h>
 
diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
index 683ba4b..f15aeac 100644
--- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
+++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
@@ -18,7 +18,7 @@
 
 #pragma once
 
-#include <vulkan/vulkan.h>
+#include "mvk_vulkan.h"
 #include <vulkan/vk_icd.h>
 #include <string>
 
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.mm b/MoltenVK/MoltenVK/Utility/MVKFoundation.cpp
similarity index 95%
rename from MoltenVK/MoltenVK/Utility/MVKFoundation.mm
rename to MoltenVK/MoltenVK/Utility/MVKFoundation.cpp
index 847c223..fab2bcc 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.mm
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.cpp
@@ -1,5 +1,5 @@
 /*
- * MVKFoundation.mm
+ * MVKFoundation.cpp
  *
  * Copyright (c) 2014-2018 The Brenwill Workshop Ltd. (http://www.brenwill.com)
  *
@@ -52,8 +52,8 @@
             CASE_RESULT(VK_ERROR_VALIDATION_FAILED_EXT)
             CASE_RESULT(VK_ERROR_INVALID_SHADER_NV)
 
-            CASE_RESULT(VK_ERROR_OUT_OF_POOL_MEMORY_KHR)
-            CASE_RESULT(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)
+            CASE_RESULT(VK_ERROR_OUT_OF_POOL_MEMORY)
+            CASE_RESULT(VK_ERROR_INVALID_EXTERNAL_HANDLE)
 
 		default:
 			sprintf(name, "UNKNOWN_VkResult(%d)", vkResult);
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index 2e1ae3e..22539cf 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -20,7 +20,7 @@
 #pragma once
 
 
-#include <vulkan/vulkan.h>
+#include "mvk_vulkan.h"
 #include "MVKLogging.h"
 #include <algorithm>
 #include <simd/simd.h>
@@ -311,11 +311,13 @@
  * Iterates through the contents of the specified Objective-C object pointer 
  * container and releases each object, and clears the container.
  */
+#ifdef __OBJC__
 template<typename C>
 void mvkReleaseContainerContents(C& container) {
     for (auto elem : container) { [elem release]; }
     container.clear();
 }
+#endif
 
 /** Removes the first occurance of the specified value from the specified container. */
 template<class C, class T>
diff --git a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm
index fee880f..cc6bbfb 100644
--- a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm
@@ -32,7 +32,7 @@
     MVKConfiguration*                           pConfiguration) {
 
 	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
-    if (pConfiguration) { *pConfiguration = mvkInst->_mvkConfig; }
+    if (pConfiguration) { *pConfiguration = *(MVKConfiguration*)mvkInst->getMoltenVKConfiguration(); }
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkSetMoltenVKConfigurationMVK(
@@ -40,7 +40,7 @@
     MVKConfiguration*                           pConfiguration) {
 
 	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
-    if (pConfiguration) { mvkInst->_mvkConfig = *pConfiguration; }
+    if (pConfiguration) { mvkInst->setMoltenVKConfiguration(pConfiguration); }
     return VK_SUCCESS;
 }
 
@@ -142,7 +142,7 @@
 	MVKDeviceConfiguration*                     pConfiguration) {
 
 	MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
-	if (pConfiguration) { *pConfiguration = mvkDev->getInstance()->_mvkConfig; }
+	if (pConfiguration) { *pConfiguration = *(MVKConfiguration*)mvkDev->getInstance()->getMoltenVKConfiguration(); }
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkSetMoltenVKDeviceConfigurationMVK(
@@ -150,7 +150,7 @@
 	MVKDeviceConfiguration*                     pConfiguration) {
 
 	MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
-	if (pConfiguration) { mvkDev->getInstance()->_mvkConfig = *pConfiguration; }
+	if (pConfiguration) { mvkDev->getInstance()->setMoltenVKConfiguration(pConfiguration); }
 	return VK_SUCCESS;
 }