Port to Fuchsia

Changes to BUILD.gn to support Fuchsia build, including adding an sdk component.

Loader uses dlopen_fuchsia to communicate with Fuchsia Vulkan loader service
that provides access to client drivers.

Support for VK_FUCHSIA_imagepipe_surface in loader wsi handling.

Change-Id: I8622c2976faf6d6b933b23dd98813f3ad5f05526
diff --git a/BUILD.gn b/BUILD.gn
index 5acbfff..7cef22b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -13,11 +13,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build_overrides/build.gni")
 import("//build_overrides/vulkan_loader.gni")
 
-# Fuchsia has non-upstream changes to the vulkan loader, so we don't want
-# to build it from upstream sources.
-assert(!is_fuchsia)
+if (is_fuchsia) {
+  import("//build/cpp/sdk_shared_library.gni")
+  import("//build/sdk/sdk_documentation.gni")
+}
 
 if (!is_android) {
   vulkan_undefine_configs = []
@@ -34,6 +36,12 @@
   if (is_clang || !is_win) {
     cflags = [ "-Wno-unused-function" ]
   }
+  if (is_fuchsia) {
+    defines += [
+      "SYSCONFDIR=\"/config\"",
+      "EXTRASYSCONFDIR=\"/pkg/data\"",
+    ]
+  }
   if (is_linux) {
     defines += [
       "SYSCONFDIR=\"/etc\"",
@@ -63,7 +71,9 @@
 }
 
 if (!is_android) {
-  if (vulkan_loader_shared) {
+  if (is_fuchsia) {
+    library_type = "sdk_shared_library"
+  } else if (vulkan_loader_shared) {
     library_type = "shared_library"
   } else {
     library_type = "static_library"
@@ -113,13 +123,85 @@
         ]
       }
     }
+    if (build_with_chromium) {
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [ "//build/config/compiler:no_chromium_code" ]
+    }
     public_deps = [
       "$vulkan_headers_dir:vulkan_headers",
     ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
     configs += [ ":vulkan_internal_config" ]
     public_configs = [ ":vulkan_loader_config" ]
     configs -= vulkan_undefine_configs
+
+    if (is_fuchsia) {
+      output_name = "vulkan"
+      category = "partner"
+
+      # The Vulkan loader's interface is defined by standard Khronos vulkan headers
+      # which can be obtained separately from the loader implementation itself.
+      no_headers = true
+
+      deps = [
+        ":dlopen_fuchsia",
+      ]
+
+      # TODO(MA-488) - while these shared libraries are needed by vulkan icd
+      # the app must provide them, so we add the deps here.
+      deps += [ "//zircon/public/lib/trace-engine" ]
+      deps += [ "//zircon/public/lib/async-default" ]
+
+      libs = [ "zircon" ]
+
+      runtime_deps = [ "//zircon/public/lib/fdio:fdio_sdk" ]
+
+      # TODO(MA-488): remove this too
+      runtime_deps += [ "//zircon/public/lib/trace-engine:trace-engine_sdk" ]
+      runtime_deps += [ "//zircon/public/lib/async-default:async-default_sdk" ]
+
+      # TODO(MA-488): The //zircon/public/lib/async-default dep gives us a dep
+      # on //zircon/public/lib/async, which uses the C++ standard library and
+      # is linked statically (built from source, though async-default is a
+      # prebuilt shared library).  So link in the C++ standard library
+      # statically just to satisfy those references.  When the async-default
+      # dep is dropped, this can change to "none" (i.e. be removed).
+      libcxx_linkage = "static"
+    }
   }
 }
+
+if (is_fuchsia) {
+  config("fuchsia_config") {
+    include_dirs = [ "fuchsia" ]
+  }
+
+  source_set("dlopen_fuchsia") {
+    public_configs = [ ":fuchsia_config" ]
+
+    sources = [
+      "fuchsia/dlopen_fuchsia.c",
+      "fuchsia/dlopen_fuchsia.h",
+    ]
+
+    deps = [
+      "//sdk/fidl/fuchsia.vulkan.loader:fuchsia.vulkan.loader_c_client",
+      "//zircon/public/lib/fdio",
+    ]
+  }
+
+  sdk_documentation("vulkan_license") {
+    name = "vulkan_license"
+    category = "public"
+
+    files = [
+      {
+        source = "LICENSE.txt"
+        dest = "LICENSE.vulkan"
+      },
+    ]
+  }
+}
+
+group("tests") {
+  # TODO(MA-684)
+}
diff --git a/README.fuchsia b/README.fuchsia
new file mode 100644
index 0000000..df31763
--- /dev/null
+++ b/README.fuchsia
@@ -0,0 +1,8 @@
+Name: Vulkan-Loader
+License: Apache 2.0
+License File: LICENSE.txt
+Upstream Git: https://github.com/KhronosGroup/Vulkan-Loader
+Description:
+
+The Khronos official Vulkan ICD desktop loader.
+
diff --git a/fuchsia/dlopen_fuchsia.c b/fuchsia/dlopen_fuchsia.c
new file mode 100644
index 0000000..b443185
--- /dev/null
+++ b/fuchsia/dlopen_fuchsia.c
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright (c) 2018 Google Inc.
+ *
+ * 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 "dlopen_fuchsia.h"
+
+#include <fcntl.h>
+#include <fuchsia/vulkan/loader/c/fidl.h>
+#include <lib/fdio/io.h>
+#include <lib/fdio/directory.h>
+#include <stdio.h>
+#include <string.h>
+#include <threads.h>
+#include <zircon/dlfcn.h>
+#include <zircon/syscalls.h>
+
+static char g_error[128] = {};
+
+const char *dlerror_fuchsia(void) { return g_error; }
+
+static zx_handle_t vulkan_loader_svc = ZX_HANDLE_INVALID;
+void connect_to_vulkan_loader_svc(void) {
+    zx_handle_t svc1, svc2;
+    if (zx_channel_create(0, &svc1, &svc2) != ZX_OK) return;
+
+    if (fdio_service_connect("/svc/" fuchsia_vulkan_loader_Loader_Name, svc1) != ZX_OK) {
+        zx_handle_close(svc2);
+        return;
+    }
+
+    vulkan_loader_svc = svc2;
+}
+
+static once_flag svc_connect_once_flag = ONCE_FLAG_INIT;
+
+void *dlopen_fuchsia(const char *name, int mode, bool driver) {
+    // First try to just dlopen() from our own namespace. This will succeed for
+    // any layers that are packaged with the application, but will fail for
+    // client drivers loaded from the system.
+    void *result;
+    if (!driver) {
+        result = dlopen(name, mode);
+        if (result != NULL) return result;
+    }
+
+    // If we couldn't find the library in our own namespace, connect to the
+    // loader service to request this library.
+    call_once(&svc_connect_once_flag, connect_to_vulkan_loader_svc);
+
+    if (vulkan_loader_svc == ZX_HANDLE_INVALID) {
+        snprintf(g_error, sizeof(g_error), "libvulkan.so:dlopen_fuchsia: no connection to loader svc\n");
+        return NULL;
+    }
+
+    zx_handle_t vmo = ZX_HANDLE_INVALID;
+    zx_status_t st = fuchsia_vulkan_loader_LoaderGet(vulkan_loader_svc, name, strlen(name), &vmo);
+    // TODO: If this call fails because the channel is closed, we may want to
+    // reconnect.
+    if (st != ZX_OK) {
+        snprintf(g_error, sizeof(g_error), "libvulkan.so:dlopen_fuchsia: Get() failed: %d\n", st);
+        return NULL;
+    }
+
+    if (vmo == ZX_HANDLE_INVALID) {
+        snprintf(g_error, sizeof(g_error), "libvulkan.so:dlopen_fuchsia: Get() returned invalid vmo\n");
+        return NULL;
+    }
+
+    result = dlopen_vmo(vmo, mode);
+    zx_handle_close(vmo);
+    if (!result) {
+        snprintf(g_error, sizeof(g_error), "%s", dlerror());
+    }
+    return result;
+}
diff --git a/fuchsia/dlopen_fuchsia.h b/fuchsia/dlopen_fuchsia.h
new file mode 100644
index 0000000..a674b8d
--- /dev/null
+++ b/fuchsia/dlopen_fuchsia.h
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (c) 2018 Google Inc.
+ *
+ * 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 <zircon/compiler.h>
+#include <stdbool.h>
+
+__BEGIN_CDECLS
+
+// If not |driver|, then the request is to load a layer.
+void *dlopen_fuchsia(const char *name, int mode, bool driver);
+const char *dlerror_fuchsia(void);
+
+__END_CDECLS
diff --git a/loader/generated/vk_loader_extensions.c b/loader/generated/vk_loader_extensions.c
index 80d80e6..d5f5f8a 100644
--- a/loader/generated/vk_loader_extensions.c
+++ b/loader/generated/vk_loader_extensions.c
@@ -2983,28 +2983,6 @@
 }
 
 
-// ---- VK_FUCHSIA_imagepipe_surface extension trampoline/terminators
-
-#ifdef VK_USE_PLATFORM_FUCHSIA
-VKAPI_ATTR VkResult VKAPI_CALL CreateImagePipeSurfaceFUCHSIA(
-    VkInstance                                  instance,
-    const VkImagePipeSurfaceCreateInfoFUCHSIA*  pCreateInfo,
-    const VkAllocationCallbacks*                pAllocator,
-    VkSurfaceKHR*                               pSurface) {
-#error("Not implemented. Likely needs to be manually generated!");
-    return disp->CreateImagePipeSurfaceFUCHSIA(instance, pCreateInfo, pAllocator, pSurface);
-}
-
-VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateImagePipeSurfaceFUCHSIA(
-    VkInstance                                  instance,
-    const VkImagePipeSurfaceCreateInfoFUCHSIA*  pCreateInfo,
-    const VkAllocationCallbacks*                pAllocator,
-    VkSurfaceKHR*                               pSurface) {
-#error("Not implemented. Likely needs to be manually generated!");
-}
-
-#endif // VK_USE_PLATFORM_FUCHSIA
-
 // ---- VK_EXT_metal_surface extension trampoline/terminators
 
 #ifdef VK_USE_PLATFORM_METAL_EXT
@@ -3898,16 +3876,6 @@
         return true;
     }
 
-    // ---- VK_FUCHSIA_imagepipe_surface extension commands
-#ifdef VK_USE_PLATFORM_FUCHSIA
-    if (!strcmp("vkCreateImagePipeSurfaceFUCHSIA", name)) {
-        *addr = (ptr_instance->enabled_known_extensions.fuchsia_imagepipe_surface == 1)
-                     ? (void *)CreateImagePipeSurfaceFUCHSIA
-                     : NULL;
-        return true;
-    }
-#endif // VK_USE_PLATFORM_FUCHSIA
-
     // ---- VK_EXT_metal_surface extension commands
 #ifdef VK_USE_PLATFORM_METAL_EXT
     if (!strcmp("vkCreateMetalSurfaceEXT", name)) {
@@ -4032,12 +4000,6 @@
         } else if (0 == strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
             ptr_instance->enabled_known_extensions.ext_debug_utils = 1;
 
-    // ---- VK_FUCHSIA_imagepipe_surface extension commands
-#ifdef VK_USE_PLATFORM_FUCHSIA
-        } else if (0 == strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME)) {
-            ptr_instance->enabled_known_extensions.fuchsia_imagepipe_surface = 1;
-#endif // VK_USE_PLATFORM_FUCHSIA
-
     // ---- VK_EXT_metal_surface extension commands
 #ifdef VK_USE_PLATFORM_METAL_EXT
         } else if (0 == strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_METAL_SURFACE_EXTENSION_NAME)) {
diff --git a/loader/generated/vk_loader_extensions.h b/loader/generated/vk_loader_extensions.h
index 954e774..a913e2a 100644
--- a/loader/generated/vk_loader_extensions.h
+++ b/loader/generated/vk_loader_extensions.h
@@ -436,7 +436,6 @@
         uint8_t ext_acquire_xlib_display : 1;
         uint8_t ext_display_surface_counter : 1;
         uint8_t ext_debug_utils : 1;
-        uint8_t fuchsia_imagepipe_surface : 1;
         uint8_t ext_metal_surface : 1;
     };
     uint64_t padding[4];
diff --git a/loader/loader.c b/loader/loader.c
index 871095d..d19fb4d 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -240,7 +240,7 @@
 }
 
 // Environment variables
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__)
 
 static inline char *loader_getenv(const char *name, const struct loader_instance *inst) {
     // No allocation of memory necessary for Linux, but we should at least touch
@@ -259,6 +259,8 @@
     // This algorithm is derived from glibc code that sets an internal
     // variable (__libc_enable_secure) if the process is running under setuid or setgid.
     return geteuid() != getuid() || getegid() != getgid() ? NULL : loader_getenv(name, inst);
+#elif defined(__Fuchsia__)
+    return loader_getenv(name, inst);
 #else
 // Linux
 #ifdef HAVE_SECURE_GETENV
@@ -2116,7 +2118,7 @@
 
     // TODO implement smarter opening/closing of libraries. For now this
     // function leaves libraries open and the scanned_icd_clear closes them
-    handle = loader_platform_open_library(filename);
+    handle = loader_platform_open_library_or_driver(filename, true);
     if (NULL == handle) {
         loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, loader_platform_open_library_error(filename));
         goto out;
@@ -3632,12 +3634,14 @@
     if (xdgdatadirs == NULL) {
         xdgdata_alloc = false;
     }
+#if !defined(__Fuchsia__)
     if (xdgconfdirs == NULL || xdgconfdirs[0] == '\0') {
         xdgconfdirs = FALLBACK_CONFIG_DIRS;
     }
     if (xdgdatadirs == NULL || xdgdatadirs[0] == '\0') {
         xdgdatadirs = FALLBACK_DATA_DIRS;
     }
+#endif
 
     // Only use HOME if XDG_DATA_HOME is not present on the system
     if (NULL == xdgdatahome) {
diff --git a/loader/loader.h b/loader/loader.h
index 36f0414..a03d1b3 100644
--- a/loader/loader.h
+++ b/loader/loader.h
@@ -332,6 +332,9 @@
 #ifdef VK_USE_PLATFORM_IOS_MVK
     bool wsi_ios_surface_enabled;
 #endif
+#ifdef VK_USE_PLATFORM_FUCHSIA
+    bool wsi_imagepipe_surface_enabled;
+#endif
     bool wsi_headless_surface_enabled;
     bool wsi_display_enabled;
     bool wsi_display_props2_enabled;
diff --git a/loader/vk_loader_platform.h b/loader/vk_loader_platform.h
index c697293..7ab99f5 100644
--- a/loader/vk_loader_platform.h
+++ b/loader/vk_loader_platform.h
@@ -28,10 +28,14 @@
 #include <WinSock2.h>
 #endif  // _WIN32
 
+#if defined(__Fuchsia__)
+#include "dlopen_fuchsia.h"
+#endif  // defined(__Fuchsia__)
+
 #include "vulkan/vk_platform.h"
 #include "vulkan/vk_sdk_platform.h"
 
-#if defined(__linux__) || defined(__APPLE__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__Fuchsia__)
 /* Linux-specific common code: */
 
 // Headers:
@@ -102,14 +106,27 @@
 
 // Dynamic Loading of libraries:
 typedef void *loader_platform_dl_handle;
-static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
+static inline loader_platform_dl_handle loader_platform_open_library_or_driver(const char *libPath, bool driver) {
+#ifdef __Fuchsia__
+    return dlopen_fuchsia(libPath, RTLD_LAZY | RTLD_LOCAL, driver);
+#else
     // When loading the library, we use RTLD_LAZY so that not all symbols have to be
     // resolved at this time (which improves performance). Note that if not all symbols
     // can be resolved, this could cause crashes later. Use the LD_BIND_NOW environment
     // variable to force all symbols to be resolved here.
     return dlopen(libPath, RTLD_LAZY | RTLD_LOCAL);
+#endif
 }
-static inline const char *loader_platform_open_library_error(const char *libPath) { return dlerror(); }
+static inline loader_platform_dl_handle loader_platform_open_library(const char *libPath) {
+    return loader_platform_open_library_or_driver(libPath, false);
+}
+static inline const char *loader_platform_open_library_error(const char *libPath) {
+#ifdef __Fuchsia__
+    return dlerror_fuchsia();
+#else
+    return dlerror();
+#endif
+}
 static inline void loader_platform_close_library(loader_platform_dl_handle library) { dlclose(library); }
 static inline void *loader_platform_get_proc_address(loader_platform_dl_handle library, const char *name) {
     assert(library);
diff --git a/loader/wsi.c b/loader/wsi.c
index e9f573f..ec9a4d1 100644
--- a/loader/wsi.c
+++ b/loader/wsi.c
@@ -1731,6 +1731,86 @@
                                                              pDisplayPlaneInfo->planeIndex, &pCapabilities->capabilities);
 }
 
+#ifdef VK_USE_PLATFORM_FUCHSIA
+
+// This is the trampoline entrypoint for CreateImagePipeSurfaceFUCHSIA
+LOADER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateImagePipeSurfaceFUCHSIA(VkInstance instance,
+                                                                             const VkImagePipeSurfaceCreateInfoFUCHSIA *pCreateInfo,
+                                                                             const VkAllocationCallbacks *pAllocator,
+                                                                             VkSurfaceKHR *pSurface) {
+    const VkLayerInstanceDispatchTable *disp;
+    disp = loader_get_instance_layer_dispatch(instance);
+    VkResult res;
+
+    res = disp->CreateImagePipeSurfaceFUCHSIA(instance, pCreateInfo, pAllocator, pSurface);
+    return res;
+}
+
+// This is the instance chain terminator function for CreateImagePipeSurfaceFUCHSIA
+VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateImagePipeSurfaceFUCHSIA(VkInstance instance,
+                                                                        const VkImagePipeSurfaceCreateInfoFUCHSIA *pCreateInfo,
+                                                                        const VkAllocationCallbacks *pAllocator,
+                                                                        VkSurfaceKHR *pSurface) {
+    VkResult vkRes = VK_SUCCESS;
+    VkIcdSurface *pIcdSurface = NULL;
+    uint32_t i = 0;
+
+    // Initialize pSurface to NULL just to be safe.
+    *pSurface = VK_NULL_HANDLE;
+    // First, check to ensure the appropriate extension was enabled:
+    struct loader_instance *ptr_instance = loader_get_instance(instance);
+    if (!ptr_instance->wsi_imagepipe_surface_enabled) {
+        loader_log(ptr_instance, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
+                   "VK_FUCHSIA_imagepipe_surface extension not enabled.  "
+                   "vkCreateImagePipeSurfaceFUCHSIA not executed!\n");
+        vkRes = VK_ERROR_EXTENSION_NOT_PRESENT;
+        goto out;
+    }
+
+    // Next, if so, proceed with the implementation of this function:
+    pIcdSurface =
+        AllocateIcdSurfaceStruct(ptr_instance, sizeof(pIcdSurface->imagepipe_surf.base), sizeof(pIcdSurface->imagepipe_surf));
+    if (pIcdSurface == NULL) {
+        vkRes = VK_ERROR_OUT_OF_HOST_MEMORY;
+        goto out;
+    }
+
+    pIcdSurface->imagepipe_surf.base.platform = VK_ICD_WSI_PLATFORM_FUCHSIA;
+
+    // Loop through each ICD and determine if they need to create a surface
+    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 (NULL != icd_term->dispatch.CreateImagePipeSurfaceFUCHSIA) {
+                vkRes = icd_term->dispatch.CreateImagePipeSurfaceFUCHSIA(icd_term->instance, pCreateInfo, pAllocator,
+                                                                         &pIcdSurface->real_icd_surfaces[i]);
+                if (VK_SUCCESS != vkRes) {
+                    goto out;
+                }
+            }
+        }
+    }
+
+    *pSurface = (VkSurfaceKHR)(pIcdSurface);
+
+out:
+
+    if (VK_SUCCESS != vkRes && NULL != pIcdSurface) {
+        if (NULL != pIcdSurface->real_icd_surfaces) {
+            i = 0;
+            for (struct loader_icd_term *icd_term = ptr_instance->icd_terms; icd_term != NULL; icd_term = icd_term->next, i++) {
+                if ((VkSurfaceKHR)NULL != pIcdSurface->real_icd_surfaces[i] && NULL != icd_term->dispatch.DestroySurfaceKHR) {
+                    icd_term->dispatch.DestroySurfaceKHR(icd_term->instance, pIcdSurface->real_icd_surfaces[i], pAllocator);
+                }
+            }
+            loader_instance_heap_free(ptr_instance, pIcdSurface->real_icd_surfaces);
+        }
+        loader_instance_heap_free(ptr_instance, pIcdSurface);
+    }
+
+    return vkRes;
+}
+#endif  // VK_USE_PLATFORM_FUCHSIA
+
 bool wsi_swapchain_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr) {
     *addr = NULL;
 
diff --git a/loader/wsi.h b/loader/wsi.h
index b09f168..8ef449f 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_FUCHSIA
+        VkIcdSurfaceImagePipe imagepipe_surf;
+#endif  // VK_USE_PLATFORM_FUCHSIA
         VkIcdSurfaceDisplay display_surf;
         VkIcdSurfaceHeadless headless_surf;
     };
@@ -173,5 +176,9 @@
 VKAPI_ATTR VkResult VKAPI_CALL terminator_GetDisplayPlaneCapabilities2KHR(VkPhysicalDevice physicalDevice,
                                                                           const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
                                                                           VkDisplayPlaneCapabilities2KHR *pCapabilities);
+#ifdef VK_USE_PLATFORM_FUCHSIA
+VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateImagePipeSurfaceFUCHSIA(VkInstance instance, const VkImagePipeSurfaceCreateInfoFUCHSIA *pCreateInfo,
+                                                                const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
+#endif
 
 #endif // WSI_H
diff --git a/scripts/loader_extension_generator.py b/scripts/loader_extension_generator.py
index 49cf720..6b8c6c3 100644
--- a/scripts/loader_extension_generator.py
+++ b/scripts/loader_extension_generator.py
@@ -39,7 +39,8 @@
                  'VK_EXT_headless_surface',
                  'VK_KHR_swapchain',
                  'VK_KHR_display_swapchain',
-                 'VK_KHR_get_display_properties2']
+                 'VK_KHR_get_display_properties2',
+                 'VK_FUCHSIA_imagepipe_surface']
 
 ADD_INST_CMDS = ['vkCreateInstance',
                  'vkEnumerateInstanceExtensionProperties',
diff --git a/vulkan.symbols.api b/vulkan.symbols.api
new file mode 100644
index 0000000..2291efa
--- /dev/null
+++ b/vulkan.symbols.api
@@ -0,0 +1,192 @@
+vkAcquireNextImage2KHR
+vkAcquireNextImageKHR
+vkAllocateCommandBuffers
+vkAllocateDescriptorSets
+vkAllocateMemory
+vkBeginCommandBuffer
+vkBindBufferMemory
+vkBindBufferMemory2
+vkBindImageMemory
+vkBindImageMemory2
+vkCmdBeginQuery
+vkCmdBeginRenderPass
+vkCmdBindDescriptorSets
+vkCmdBindIndexBuffer
+vkCmdBindPipeline
+vkCmdBindVertexBuffers
+vkCmdBlitImage
+vkCmdClearAttachments
+vkCmdClearColorImage
+vkCmdClearDepthStencilImage
+vkCmdCopyBuffer
+vkCmdCopyBufferToImage
+vkCmdCopyImage
+vkCmdCopyImageToBuffer
+vkCmdCopyQueryPoolResults
+vkCmdDispatch
+vkCmdDispatchBase
+vkCmdDispatchIndirect
+vkCmdDraw
+vkCmdDrawIndexed
+vkCmdDrawIndexedIndirect
+vkCmdDrawIndirect
+vkCmdEndQuery
+vkCmdEndRenderPass
+vkCmdExecuteCommands
+vkCmdFillBuffer
+vkCmdNextSubpass
+vkCmdPipelineBarrier
+vkCmdPushConstants
+vkCmdResetEvent
+vkCmdResetQueryPool
+vkCmdResolveImage
+vkCmdSetBlendConstants
+vkCmdSetDepthBias
+vkCmdSetDepthBounds
+vkCmdSetDeviceMask
+vkCmdSetEvent
+vkCmdSetLineWidth
+vkCmdSetScissor
+vkCmdSetStencilCompareMask
+vkCmdSetStencilReference
+vkCmdSetStencilWriteMask
+vkCmdSetViewport
+vkCmdUpdateBuffer
+vkCmdWaitEvents
+vkCmdWriteTimestamp
+vkCreateBuffer
+vkCreateBufferView
+vkCreateCommandPool
+vkCreateComputePipelines
+vkCreateDescriptorPool
+vkCreateDescriptorSetLayout
+vkCreateDescriptorUpdateTemplate
+vkCreateDevice
+vkCreateDisplayModeKHR
+vkCreateDisplayPlaneSurfaceKHR
+vkCreateEvent
+vkCreateFence
+vkCreateFramebuffer
+vkCreateGraphicsPipelines
+vkCreateImage
+vkCreateImagePipeSurfaceFUCHSIA
+vkCreateImageView
+vkCreateInstance
+vkCreatePipelineCache
+vkCreatePipelineLayout
+vkCreateQueryPool
+vkCreateRenderPass
+vkCreateSampler
+vkCreateSamplerYcbcrConversion
+vkCreateSemaphore
+vkCreateShaderModule
+vkCreateSharedSwapchainsKHR
+vkCreateSwapchainKHR
+vkDestroyBuffer
+vkDestroyBufferView
+vkDestroyCommandPool
+vkDestroyDescriptorPool
+vkDestroyDescriptorSetLayout
+vkDestroyDescriptorUpdateTemplate
+vkDestroyDevice
+vkDestroyEvent
+vkDestroyFence
+vkDestroyFramebuffer
+vkDestroyImage
+vkDestroyImageView
+vkDestroyInstance
+vkDestroyPipeline
+vkDestroyPipelineCache
+vkDestroyPipelineLayout
+vkDestroyQueryPool
+vkDestroyRenderPass
+vkDestroySampler
+vkDestroySamplerYcbcrConversion
+vkDestroySemaphore
+vkDestroyShaderModule
+vkDestroySurfaceKHR
+vkDestroySwapchainKHR
+vkDeviceWaitIdle
+vkEndCommandBuffer
+vkEnumerateDeviceExtensionProperties
+vkEnumerateDeviceLayerProperties
+vkEnumerateInstanceExtensionProperties
+vkEnumerateInstanceLayerProperties
+vkEnumerateInstanceVersion
+vkEnumeratePhysicalDeviceGroups
+vkEnumeratePhysicalDevices
+vkFlushMappedMemoryRanges
+vkFreeCommandBuffers
+vkFreeDescriptorSets
+vkFreeMemory
+vkGetBufferMemoryRequirements
+vkGetBufferMemoryRequirements2
+vkGetDescriptorSetLayoutSupport
+vkGetDeviceGroupPeerMemoryFeatures
+vkGetDeviceGroupPresentCapabilitiesKHR
+vkGetDeviceGroupSurfacePresentModesKHR
+vkGetDeviceMemoryCommitment
+vkGetDeviceProcAddr
+vkGetDeviceQueue
+vkGetDeviceQueue2
+vkGetDisplayModeProperties2KHR
+vkGetDisplayModePropertiesKHR
+vkGetDisplayPlaneCapabilities2KHR
+vkGetDisplayPlaneCapabilitiesKHR
+vkGetDisplayPlaneSupportedDisplaysKHR
+vkGetEventStatus
+vkGetFenceStatus
+vkGetImageMemoryRequirements
+vkGetImageMemoryRequirements2
+vkGetImageSparseMemoryRequirements
+vkGetImageSparseMemoryRequirements2
+vkGetImageSubresourceLayout
+vkGetInstanceProcAddr
+vkGetPhysicalDeviceDisplayPlaneProperties2KHR
+vkGetPhysicalDeviceDisplayPlanePropertiesKHR
+vkGetPhysicalDeviceDisplayProperties2KHR
+vkGetPhysicalDeviceDisplayPropertiesKHR
+vkGetPhysicalDeviceExternalBufferProperties
+vkGetPhysicalDeviceExternalFenceProperties
+vkGetPhysicalDeviceExternalSemaphoreProperties
+vkGetPhysicalDeviceFeatures
+vkGetPhysicalDeviceFeatures2
+vkGetPhysicalDeviceFormatProperties
+vkGetPhysicalDeviceFormatProperties2
+vkGetPhysicalDeviceImageFormatProperties
+vkGetPhysicalDeviceImageFormatProperties2
+vkGetPhysicalDeviceMemoryProperties
+vkGetPhysicalDeviceMemoryProperties2
+vkGetPhysicalDevicePresentRectanglesKHR
+vkGetPhysicalDeviceProperties
+vkGetPhysicalDeviceProperties2
+vkGetPhysicalDeviceQueueFamilyProperties
+vkGetPhysicalDeviceQueueFamilyProperties2
+vkGetPhysicalDeviceSparseImageFormatProperties
+vkGetPhysicalDeviceSparseImageFormatProperties2
+vkGetPhysicalDeviceSurfaceCapabilitiesKHR
+vkGetPhysicalDeviceSurfaceFormatsKHR
+vkGetPhysicalDeviceSurfacePresentModesKHR
+vkGetPhysicalDeviceSurfaceSupportKHR
+vkGetPipelineCacheData
+vkGetQueryPoolResults
+vkGetRenderAreaGranularity
+vkGetSwapchainImagesKHR
+vkInvalidateMappedMemoryRanges
+vkMapMemory
+vkMergePipelineCaches
+vkQueueBindSparse
+vkQueuePresentKHR
+vkQueueSubmit
+vkQueueWaitIdle
+vkResetCommandBuffer
+vkResetCommandPool
+vkResetDescriptorPool
+vkResetEvent
+vkResetFences
+vkSetEvent
+vkTrimCommandPool
+vkUnmapMemory
+vkUpdateDescriptorSetWithTemplate
+vkUpdateDescriptorSets
+vkWaitForFences