loader: Add support for metal surfaces

The adds support for the VK_EXT_metal_surface extension

Change-Id: Ibb1c21e19fb053fe332ea075c3d32de59f7f8881
diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt
index ed78054..8c2cedb 100644
--- a/loader/CMakeLists.txt
+++ b/loader/CMakeLists.txt
@@ -38,6 +38,7 @@
     add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR)
 elseif(APPLE)
     add_definitions(-DVK_USE_PLATFORM_MACOS_MVK)
+    add_definitions(-DVK_USE_PLATFORM_METAL_EXT)
 elseif(UNIX AND NOT APPLE) # i.e.: Linux
     if(BUILD_WSI_XCB_SUPPORT)
         add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
diff --git a/loader/loader.h b/loader/loader.h
index 36f0414..8d6b4c4 100644
--- a/loader/loader.h
+++ b/loader/loader.h
@@ -333,6 +333,9 @@
     bool wsi_ios_surface_enabled;
 #endif
     bool wsi_headless_surface_enabled;
+#if defined(VK_USE_PLATFORM_METAL_EXT)
+    bool wsi_metal_surface_enabled;
+#endif
     bool wsi_display_enabled;
     bool wsi_display_props2_enabled;
 };
diff --git a/loader/wsi.c b/loader/wsi.c
index e9f573f..f8d8e53 100644
--- a/loader/wsi.c
+++ b/loader/wsi.c
@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2015-2016 The Khronos Group Inc.
- * Copyright (c) 2015-2016 Valve Corporation
- * Copyright (c) 2015-2016 LunarG, Inc.
+ * Copyright (c) 2015-2016, 2019 The Khronos Group Inc.
+ * Copyright (c) 2015-2016, 2019 Valve Corporation
+ * Copyright (c) 2015-2016, 2019 LunarG, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
  * Author: Jon Ashburn <jon@lunarg.com>
  * Author: Ian Elliott <ianelliott@google.com>
  * Author: Mark Lobodzinski <mark@lunarg.com>
+ * Author: Lenny Komow <lenny@lunarg.com>
  */
 
 #ifndef _GNU_SOURCE
@@ -60,9 +61,11 @@
 #ifdef VK_USE_PLATFORM_IOS_MVK
     ptr_instance->wsi_ios_surface_enabled = false;
 #endif  // VK_USE_PLATFORM_IOS_MVK
-
     ptr_instance->wsi_display_enabled = false;
     ptr_instance->wsi_display_props2_enabled = false;
+#ifdef VK_USE_PLATFORM_METAL_EXT
+    ptr_instance->wsi_metal_surface_enabled = false;
+#endif // VK_USE_PLATFORM_METAL_EXT
 
     for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
         if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_SURFACE_EXTENSION_NAME) == 0) {
@@ -115,6 +118,12 @@
             ptr_instance->wsi_headless_surface_enabled = true;
             continue;
         }
+#if defined(VK_USE_PLATFORM_METAL_EXT)
+        if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_METAL_SURFACE_EXTENSION_NAME) == 0) {
+            ptr_instance->wsi_metal_surface_enabled = true;
+            continue;
+        }
+#endif
         if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_KHR_DISPLAY_EXTENSION_NAME) == 0) {
             ptr_instance->wsi_display_enabled = true;
             continue;
@@ -1169,6 +1178,72 @@
 
 #endif  // VK_USE_PLATFORM_IOS_MVK
 
+#if defined(VK_USE_PLATFORM_METAL_EXT)
+
+LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateMetalSurfaceEXT(VkInstance instance,
+                                                                     const VkMetalSurfaceCreateInfoEXT *pCreateInfo,
+                                                                     const VkAllocationCallbacks *pAllocator,
+                                                                     VkSurfaceKHR *pSurface) {
+    const VkLayerInstanceDispatchTable *disp = loader_get_instance_layer_dispatch(instance);
+    return disp->CreateMetalSurfaceEXT(instance, pCreateInfo, pAllocator, pSurface);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMetalSurfaceEXT(VkInstance instance, const VkMetalSurfaceCreateInfoEXT *pCreateInfo,
+                                                                const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
+    VkResult result = VK_SUCCESS;
+    VkIcdSurface *icd_surface = NULL;
+    uint32_t i;
+
+    // First, check to ensure the appropriate extension was enabled:
+    struct loader_instance *ptr_instance = loader_get_instance(instance);
+    if (!ptr_instance->wsi_metal_surface_enabled) {
+        loader_log(ptr_instance, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
+                   "VK_EXT_metal_surface extension not enabled. vkCreateMetalSurfaceEXT will not be executed.\n");
+    }
+
+    // Next, if so, proceed with the implementation of this function:
+    icd_surface = AllocateIcdSurfaceStruct(ptr_instance, sizeof(icd_surface->metal_surf.base), sizeof(icd_surface->metal_surf));
+    if (icd_surface == NULL) {
+        result = VK_ERROR_OUT_OF_HOST_MEMORY;
+        goto out;
+    }
+
+    icd_surface->metal_surf.base.platform = VK_ICD_WSI_PLATFORM_METAL;
+    icd_surface->metal_surf.pLayer = pCreateInfo->pLayer;
+
+    // Loop through each ICD and determine if they need to create a surface
+    i = 0;
+    for (struct loader_icd_term *icd_term = ptr_instance->icd_terms; icd_term != NULL; icd_term = icd_term->next, ++i) {
+        if (icd_term->scanned_icd->interface_version >= ICD_VER_SUPPORTS_ICD_SURFACE_KHR) {
+            if (icd_term->dispatch.CreateMetalSurfaceEXT != NULL) {
+                result = icd_term->dispatch.CreateMetalSurfaceEXT(icd_term->instance, pCreateInfo, pAllocator,
+                                                                  &icd_surface->real_icd_surfaces[i]);
+                if (result != VK_SUCCESS) {
+                    goto out;
+                }
+            }
+        }
+    }
+    *pSurface = (VkSurfaceKHR)icd_surface;
+
+out:
+    if (result != VK_SUCCESS && icd_surface != NULL) {
+        if (icd_surface->real_icd_surfaces != NULL) {
+            i = 0;
+            for (struct loader_icd_term *icd_term = ptr_instance->icd_terms; icd_term != NULL; icd_term = icd_term->next, ++i) {
+                if (icd_surface->real_icd_surfaces[i] == VK_NULL_HANDLE && icd_term->dispatch.DestroySurfaceKHR != NULL) {
+                    icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, icd_surface->real_icd_surfaces[i], pAllocator);
+                }
+            }
+            loader_instance_heap_free(ptr_instance, icd_surface->real_icd_surfaces);
+        }
+        loader_instance_heap_free(ptr_instance, icd_surface);
+    }
+    return result;
+}
+
+#endif
+
 // Functions for the VK_KHR_display instance extension:
 LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice,
                                                                                      uint32_t *pPropertyCount,
@@ -1882,6 +1957,14 @@
         return true;
     }
 
+#if defined(VK_USE_PLATFORM_METAL_EXT)
+    // Functions for the VK_MVK_macos_surface extension:
+    if (!strcmp("vkCreateMetalSurfaceEXT", name)) {
+        *addr = ptr_instance->wsi_metal_surface_enabled ? (void *)vkCreateMetalSurfaceEXT : NULL;
+        return true;
+    }
+#endif // VK_USE_PLATFORM_METAL_EXT
+
     // Functions for VK_KHR_display extension:
     if (!strcmp("vkGetPhysicalDeviceDisplayPropertiesKHR", name)) {
         *addr = ptr_instance->wsi_display_enabled ? (void *)vkGetPhysicalDeviceDisplayPropertiesKHR : NULL;
diff --git a/loader/wsi.h b/loader/wsi.h
index b09f168..3e44efa 100644
--- a/loader/wsi.h
+++ b/loader/wsi.h
@@ -42,6 +42,9 @@
 #ifdef VK_USE_PLATFORM_MACOS_MVK
         VkIcdSurfaceMacOS macos_surf;
 #endif  // VK_USE_PLATFORM_MACOS_MVK
+#ifdef VK_USE_PLATFORM_METAL_EXT
+        VkIcdSurfaceMetal metal_surf;
+#endif // VK_USE_PLATFORM_METAL_EXT
         VkIcdSurfaceDisplay display_surf;
         VkIcdSurfaceHeadless headless_surf;
     };
@@ -126,6 +129,10 @@
 VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateIOSSurfaceMVK(VkInstance instance, const VkIOSSurfaceCreateInfoMVK *pCreateInfo,
                                                               const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
 #endif
+#if defined(VK_USE_PLATFORM_METAL_EXT)
+VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateMetalSurfaceEXT(VkInstance instance, const VkMetalSurfaceCreateInfoEXT *pCreateInfo,
+                                                                const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
+#endif
 VKAPI_ATTR VkResult VKAPI_CALL terminator_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice,
                                                                                 uint32_t *pPropertyCount,
                                                                                 VkDisplayPropertiesKHR *pProperties);
diff --git a/scripts/loader_extension_generator.py b/scripts/loader_extension_generator.py
index 49cf720..90ca158 100644
--- a/scripts/loader_extension_generator.py
+++ b/scripts/loader_extension_generator.py
@@ -37,6 +37,7 @@
                  'VK_MVK_macos_surface',
                  'VK_MVK_ios_surface',
                  'VK_EXT_headless_surface',
+                 'VK_EXT_metal_surface',
                  'VK_KHR_swapchain',
                  'VK_KHR_display_swapchain',
                  'VK_KHR_get_display_properties2']