Merge pull request #654 from billhollings/master

Support VK_EXT_metal_surface extension.
diff --git a/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm
index e922cf4..cf29614 100644
--- a/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm
+++ b/Demos/LunarG-VulkanSamples/API-Samples/iOS/DemoViewController.mm
@@ -32,7 +32,7 @@
  * Initialize sample from view, and resize view in accordance with the sample. 
  */
 void init_window(struct sample_info &info) {
-	info.window = sampleView;
+	info.window = sampleView.layer;
 	sampleView.bounds = CGRectMake(0, 0, info.width, info.height);
 }
 
diff --git a/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm
index 7b1bb64..36a55fe 100644
--- a/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm
+++ b/Demos/LunarG-VulkanSamples/API-Samples/macOS/DemoViewController.mm
@@ -33,7 +33,7 @@
  * Initialize sample from view, and resize view in accordance with the sample.
  */
 void init_window(struct sample_info &info) {
-	info.window = sampleView;
+	info.window = sampleView.layer;
 	sampleView.bounds = CGRectMake(0, 0, info.width, info.height);
 }
 
diff --git a/Demos/LunarG-VulkanSamples/Cube/iOS/DemoViewController.m b/Demos/LunarG-VulkanSamples/Cube/iOS/DemoViewController.m
index 9706ebc..e0cce29 100644
--- a/Demos/LunarG-VulkanSamples/Cube/iOS/DemoViewController.m
+++ b/Demos/LunarG-VulkanSamples/Cube/iOS/DemoViewController.m
@@ -43,7 +43,7 @@
 	self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale;
 
 	const char* arg = "cube";
-	demo_main(&demo, self.view, 1, &arg);
+	demo_main(&demo, self.view.layer, 1, &arg);
 	demo_draw(&demo);
 
 	uint32_t fps = 60;
diff --git a/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m b/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m
index 5c6fa42..6018369 100644
--- a/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m
+++ b/Demos/LunarG-VulkanSamples/Cube/macOS/DemoViewController.m
@@ -43,7 +43,7 @@
 
 	self.view.wantsLayer = YES;		// Back the view with a layer created by the makeBackingLayer method.
 	const char* arg = "cube";
-	demo_main(&demo, self.view, 1, &arg);
+	demo_main(&demo, self.view.layer, 1, &arg);
 
 	CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
 	CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, &demo);
diff --git a/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp
index 263f004..32e3ae9 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp
+++ b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.cpp
@@ -48,12 +48,7 @@
     _profile_start_time = _current_time;
     _profile_present_count = 0;
 
-#ifdef VK_USE_PLATFORM_IOS_MVK
-    instance_extensions_.push_back(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
-#endif
-#ifdef VK_USE_PLATFORM_MACOS_MVK
-    instance_extensions_.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
-#endif
+    instance_extensions_.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
 
     init_vk();
 }
@@ -78,22 +73,12 @@
     VkSurfaceKHR surface;
 
     VkResult err;
-#ifdef VK_USE_PLATFORM_IOS_MVK
-    VkIOSSurfaceCreateInfoMVK surface_info;
-    surface_info.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
+    VkMetalSurfaceCreateInfoEXT surface_info;
+    surface_info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
     surface_info.pNext = NULL;
     surface_info.flags = 0;
-    surface_info.pView = _view;
-    err = vkCreateIOSSurfaceMVK(instance, &surface_info, NULL, &surface);
-#endif
-#ifdef VK_USE_PLATFORM_MACOS_MVK
-    VkMacOSSurfaceCreateInfoMVK surface_info;
-    surface_info.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
-    surface_info.pNext = NULL;
-    surface_info.flags = 0;
-    surface_info.pView = _view;
-    err = vkCreateMacOSSurfaceMVK(instance, &surface_info, NULL, &surface);
-#endif
+    surface_info.pLayer = _caMetalLayer;
+    err = vkCreateMetalSurfaceEXT(instance, &surface_info, NULL, &surface);
     assert(!err);
 
     return surface;
@@ -124,8 +109,8 @@
     }
 }
 
-void ShellMVK::run(void* view) {
-    _view = view;       // not retained
+void ShellMVK::run(void* caMetalLayer) {
+    _caMetalLayer = caMetalLayer;       // not retained
     create_context();
     resize_swapchain(settings_.initial_width, settings_.initial_height);
 }
diff --git a/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h
index 5948b06..4309920 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h
+++ b/Demos/LunarG-VulkanSamples/Hologram/ShellMVK.h
@@ -49,7 +49,7 @@
     void quit() { }
 
 protected:
-    void* _view;
+    void* _caMetalLayer;
     PosixTimer _timer;
     double _current_time;
     double _profile_start_time;
diff --git a/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm
index 7d0945a..bbfe4c1 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm
+++ b/Demos/LunarG-VulkanSamples/Hologram/iOS/DemoViewController.mm
@@ -45,12 +45,12 @@
 	self.view.contentScaleFactor = UIScreen.mainScreen.nativeScale;
 
     std::vector<std::string> args;
-  args.push_back("-p");           // Use push constants
+    args.push_back("-p");           // Use push constants
 //  args.push_back("-s");           // Use a single thread
     _game = new Hologram(args);
 
     _shell = new ShellMVK(*_game);
-    _shell->run(self.view);
+    _shell->run(self.view.layer);
 
 	uint32_t fps = 60;
 	_displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderLoop)];
diff --git a/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm b/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm
index e30ee21..6e169f0 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm
+++ b/Demos/LunarG-VulkanSamples/Hologram/macOS/DemoViewController.mm
@@ -46,12 +46,12 @@
 	self.view.wantsLayer = YES;		// Back the view with a layer created by the makeBackingLayer method.
 
     std::vector<std::string> args;
-//  args.push_back("-p");           // Uncomment to use push constants
+    args.push_back("-p");           // Uncomment to use push constants
 //  args.push_back("-s");           // Uncomment to use a single thread
     _game = new Hologram(args);
 
     _shell = new ShellMVK(*_game);
-    _shell->run(self.view);
+    _shell->run(self.view.layer);
 
 	CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
 	CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, _shell);
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index d657a65..1b36bfb 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -253,36 +253,23 @@
 - `VK_EXT_debug_utils`
 - `VK_EXT_host_query_reset`
 - `VK_EXT_memory_budget`
+- `VK_EXT_metal_surface`
 - `VK_EXT_shader_viewport_index_layer`
 - `VK_EXT_vertex_attribute_divisor`
 - `VK_EXTX_portability_subset`
-- `VK_MVK_ios_surface` (iOS)
-- `VK_MVK_macos_surface` (macOS)
+- `VK_MVK_ios_surface` *(iOS) (Obsolete. Use `VK_EXT_metal_surface` instead.)*
+- `VK_MVK_macos_surface` *(macOS) (Obsolete. Use `VK_EXT_metal_surface` instead.)*
 - `VK_MVK_moltenvk`
 - `VK_AMD_gpu_shader_half_float`
 - `VK_AMD_negative_viewport_height`
-- `VK_IMG_format_pvrtc` (iOS)
+- `VK_IMG_format_pvrtc` *(iOS)*
 - `VK_NV_glsl_shader`
 
-In order to visibly display your content on *iOS* or *macOS*, you must enable the `VK_MVK_ios_surface` 
-or `VK_MVK_macos_surface` extension, respectively, and use the functions defined for that extension
-to create a *Vulkan* rendering surface.
-
-You can enable each of these extensions by defining the `VK_USE_PLATFORM_IOS_MVK` or 
-`VK_USE_PLATFORM_MACOS_MVK` guard macro in your compiler build settings. See the description
-of the `mvk_vulkan.h` file below for a convenient way to enable these extensions automatically.
-
-When using the `VK_MVK_macos_surface ` extension, the `pView` member of the `VkMacOSSurfaceCreateInfoMVK` 
-structure passed in the `vkCreateMacOSSurfaceMVK` function can be either an `NSView` whose layer is a 
-`CAMetalLayer`, or the `CAMetalLayer` itself. Passing the `CAMetalLayer` itself is recommended when 
-calling the `vkCreateMacOSSurfaceMVK` function from outside the main application thread, as `NSView` 
-should only be accessed from the main application thread.
-
-When using the `VK_MVK_ios_surface ` extension, the `pView` member of the `VkIOSSurfaceCreateInfoMVK` 
-structure passed in the `vkCreateIOSSurfaceMVK` function can be either a `UIView` whose layer is a 
-`CAMetalLayer`, or the `CAMetalLayer` itself. Passing the `CAMetalLayer` itself is recommended when 
-calling the `vkCreateIOSSurfaceMVK ` function from outside the main application thread, as `UIView` 
-should only be accessed from the main application thread.
+In order to visibly display your content on *iOS* or *macOS*, you must enable the `VK_EXT_metal_surface` 
+extension, and use the function defined in that extension to create a *Vulkan* rendering surface.
+You can enable the `VK_EXT_metal_surface` extension by defining the `VK_USE_PLATFORM_METAL_EXT` 
+guard macro in your compiler build settings. See the description of the `mvk_vulkan.h` file below for 
+a convenient way to enable this extension automatically.
 
 
 <a name="moltenvk_extension"></a>
@@ -308,10 +295,8 @@
    enabled for *iOS* or *macOS*. Use this header file in place of the `vulkan.h` header file, 
    where access to a **MoltenVK** platform surface extension is required.
    
-   - When building for *iOS*, the `mvk_vulkan.h` header file automatically enables the 
-    `VK_USE_PLATFORM_IOS_MVK` build setting and `VK_MVK_ios_surface` *Vulkan* extension.
-   - When building for *macOS*, the `mvk_vulkan.h` header file automatically enables the
-    `VK_USE_PLATFORM_MACOS_MVK` build setting and `VK_MVK_macos_surface` *Vulkan* extension.
+   The `mvk_vulkan.h` header file automatically enables the `VK_USE_PLATFORM_METAL_EXT` 
+   build setting and `VK_EXT_metal_surface` *Vulkan* extension.
   
 - `mvk_datatypes.h` - Contains helpful functions for converting between *Vulkan* and *Metal* data types.
   You do not need to use this functionality to use **MoltenVK**, as **MoltenVK** converts between 
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 27cc42c..18dd946 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -18,6 +18,8 @@
 
 Released TBD
 
+- Add support for extensions:
+	- `VK_EXT_metal_surface`
 - For shaders created directly from MSL, set function name from 
   `VkPipelineShaderStageCreateInfo::pName`.
 - On iOS GPU family 2 and earlier, support immutable depth-compare samplers 
diff --git a/MoltenVK/MoltenVK/API/mvk_vulkan.h b/MoltenVK/MoltenVK/API/mvk_vulkan.h
index 1277064..aba9865 100644
--- a/MoltenVK/MoltenVK/API/mvk_vulkan.h
+++ b/MoltenVK/MoltenVK/API/mvk_vulkan.h
@@ -18,11 +18,13 @@
 
 
 /** 
- * This is a convenience header file that loads vulkan.h with the appropriate MoltenVK
- * Vulkan platform extensions automatically enabled for iOS or macOS.
+ * This is a convenience header file that loads vulkan.h with the appropriate Vulkan platform extensions.
  *
- * When building for iOS, this header automatically enables the VK_MVK_ios_surface Vulkan extension.
- * When building for macOS, this header automatically enables the VK_MVK_macos_surface Vulkan extension.
+ * This header automatically enables the VK_EXT_metal_surface Vulkan extension.
+ *
+ * When building for iOS, this header also automatically enables the obsolete VK_MVK_ios_surface Vulkan extension.
+ * When building for macOS, this header also automatically enables the obsolete VK_MVK_macos_surface Vulkan extension.
+ * Both of these extensions are obsolete. Consider using the portable VK_EXT_metal_surface extension instead.
  */
 
 #ifndef __mvk_vulkan_h_
@@ -31,6 +33,8 @@
 
 #include <Availability.h>
 
+#define VK_USE_PLATFORM_METAL_EXT				1
+
 #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
 #	define VK_USE_PLATFORM_IOS_MVK				1
 #endif
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
index e0a355d..6784567 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -88,6 +88,9 @@
 	/** Returns the driver layer. */
 	MVKLayer* getDriverLayer() { return MVKLayerManager::globalManager()->getDriverLayer(); }
 
+	MVKSurface* createSurface(const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
+							  const VkAllocationCallbacks* pAllocator);
+
 	MVKSurface* createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
 							  const VkAllocationCallbacks* pAllocator);
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index 1958eb0..aae5cf4 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -70,6 +70,11 @@
 	return result;
 }
 
+MVKSurface* MVKInstance::createSurface(const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
+									   const VkAllocationCallbacks* pAllocator) {
+	return new MVKSurface(this, pCreateInfo, pAllocator);
+}
+
 MVKSurface* MVKInstance::createSurface(const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
 									   const VkAllocationCallbacks* pAllocator) {
 	return new MVKSurface(this, pCreateInfo, pAllocator);
@@ -540,6 +545,7 @@
 	ADD_INST_EXT_ENTRY_POINT(vkCreateDebugUtilsMessengerEXT, EXT_DEBUG_UTILS);
 	ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugUtilsMessengerEXT, EXT_DEBUG_UTILS);
 	ADD_INST_EXT_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkCreateMetalSurfaceEXT, EXT_METAL_SURFACE);
 
 #ifdef VK_USE_PLATFORM_IOS_MVK
 	ADD_INST_EXT_ENTRY_POINT(vkCreateIOSSurfaceMVK, MVK_IOS_SURFACE);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
index 59f8b73..f34df5a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
@@ -66,6 +66,10 @@
 #pragma mark Construction
 
 	MVKSurface(MVKInstance* mvkInstance,
+			   const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
+			   const VkAllocationCallbacks* pAllocator);
+
+	MVKSurface(MVKInstance* mvkInstance,
 			   const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
 			   const VkAllocationCallbacks* pAllocator);
 
@@ -73,6 +77,7 @@
 
 protected:
 	void propogateDebugName() override {}
+	void initLayerObserver();
 
 	MVKInstance* _mvkInstance;
 	CAMetalLayer* _mtlCAMetalLayer;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm
index f2f5596..3dd8118 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm
@@ -23,16 +23,28 @@
 #include "MVKOSExtensions.h"
 #import "MVKBlockObserver.h"
 
-#define STR(NAME) #NAME
+// We need to double-dereference the name to first convert to the platform symbol, then to a string.
+#define STR_PLATFORM(NAME) #NAME
+#define STR(NAME) STR_PLATFORM(NAME)
 
 
 #pragma mark MVKSurface
 
+MVKSurface::MVKSurface(MVKInstance* mvkInstance,
+					   const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
+					   const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) {
+
+	_mtlCAMetalLayer = (CAMetalLayer*)[pCreateInfo->pLayer retain];
+	initLayerObserver();
+}
+
 // pCreateInfo->pView can be either a CAMetalLayer or a view (NSView/UIView).
 MVKSurface::MVKSurface(MVKInstance* mvkInstance,
 					   const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo,
 					   const VkAllocationCallbacks* pAllocator) : _mvkInstance(mvkInstance) {
 
+	MVKLogInfo("%s(): This function is obsolete. Consider using the vkCreateMetalSurfaceEXT() function from the VK_EXT_metal_surface extension instead.", STR(vkCreate_PLATFORM_SurfaceMVK));
+
 	// Get the platform object contained in pView
 	id<NSObject> obj = (id<NSObject>)pCreateInfo->pView;
 
@@ -45,30 +57,34 @@
 		obj = ((PLATFORM_VIEW_CLASS*)obj).layer;
 	}
 
-	_layerObserver = nil;
-
 	// Confirm that we were provided with a CAMetalLayer
 	if ([obj isKindOfClass: [CAMetalLayer class]]) {
 		_mtlCAMetalLayer = (CAMetalLayer*)[obj retain];		// retained
-		if ([_mtlCAMetalLayer.delegate isKindOfClass: [PLATFORM_VIEW_CLASS class]]) {
-			// Sometimes, the owning view can replace its CAMetalLayer. In that case, the client
-			// needs to recreate the surface.
-			_layerObserver = [MVKBlockObserver observerWithBlock: ^(NSString* path, id, NSDictionary*, void*) {
-				if ( ![path isEqualToString: @"layer"] ) { return; }
-				std::lock_guard<std::mutex> lock(this->_lock);
-				[this->_mtlCAMetalLayer release];
-				this->_mtlCAMetalLayer = nil;
-				this->setConfigurationResult(VK_ERROR_SURFACE_LOST_KHR);
-				[this->_layerObserver release];
-				this->_layerObserver = nil;
-			} forObject: _mtlCAMetalLayer.delegate atKeyPath: @"layer"];
-		}
 	} else {
 		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED,
 										   "%s(): On-screen rendering requires a layer of type CAMetalLayer.",
 										   STR(vkCreate_PLATFORM_SurfaceMVK)));
 		_mtlCAMetalLayer = nil;
 	}
+
+	initLayerObserver();
+}
+
+// Sometimes, the owning view can replace its CAMetalLayer. In that case, the client needs to recreate the surface.
+void MVKSurface::initLayerObserver() {
+
+	_layerObserver = nil;
+	if ( ![_mtlCAMetalLayer.delegate isKindOfClass: [PLATFORM_VIEW_CLASS class]] ) { return; }
+
+	_layerObserver = [MVKBlockObserver observerWithBlock: ^(NSString* path, id, NSDictionary*, void*) {
+		if ( ![path isEqualToString: @"layer"] ) { return; }
+		std::lock_guard<std::mutex> lock(this->_lock);
+		[this->_mtlCAMetalLayer release];
+		this->_mtlCAMetalLayer = nil;
+		this->setConfigurationResult(VK_ERROR_SURFACE_LOST_KHR);
+		[this->_layerObserver release];
+		this->_layerObserver = nil;
+	} forObject: _mtlCAMetalLayer.delegate atKeyPath: @"layer"];
 }
 
 MVKSurface::~MVKSurface() {
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 5e64a66..c066bd6 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -57,6 +57,7 @@
 MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS)
 MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET)
 MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET)
+MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE)
 MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER)
 MVK_EXTENSION(EXT_vertex_attribute_divisor, EXT_VERTEX_ATTRIBUTE_DIVISOR)
 MVK_EXTENSION(EXTX_portability_subset, EXTX_PORTABILITY_SUBSET)
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 6c4610a..f4875b2 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -2608,6 +2608,23 @@
 	MVKTraceVulkanCallEnd();
 }
 
+#pragma mark -
+#pragma mark VK_EXT_metal_surface extension
+
+MVK_PUBLIC_SYMBOL VkResult vkCreateMetalSurfaceEXT(
+	VkInstance                                  instance,
+	const VkMetalSurfaceCreateInfoEXT*          pCreateInfo,
+	const VkAllocationCallbacks*                pAllocator,
+	VkSurfaceKHR*                               pSurface) {
+
+	MVKTraceVulkanCallStart();
+	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
+	MVKSurface* mvkSrfc = mvkInst->createSurface(pCreateInfo, pAllocator);
+	*pSurface = (VkSurfaceKHR)mvkSrfc;
+	VkResult rslt = mvkSrfc->getConfigurationResult();
+	MVKTraceVulkanCallEnd();
+	return rslt;
+}
 
 #pragma mark -
 #pragma mark iOS & macOS surface extensions