Merge tag 'vulkan-sdk-1.3.268.0'

Bug:b/319117470

Change-Id: Iace0288ab6fdfc0d711f20f745122498de071452
diff --git a/BUILD.gn b/BUILD.gn
index 760356b..c914caa 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -44,14 +44,16 @@
       "-Wno-conversion",
       "-Wno-extra-semi",
       "-Wno-sign-compare",
+      "-Wno-strict-prototypes",
       "-Wno-unreachable-code",
       "-Wno-unused-function",
       "-Wno-unused-variable",
+      "-Wno-write-strings",
     ]
   }
   if (is_fuchsia) {
     defines += [
-      "SYSCONFDIR=\"/config\"",
+      "SYSCONFDIR=\"/vulkan-loader-configuration\"",
       "EXTRASYSCONFDIR=\"/pkg/data\"",
     ]
   }
@@ -87,9 +89,13 @@
     # assume secure_getenv() is available
     defines += [
       "HAVE_SECURE_GETENV",
+      "_POSIX_C_SOURCE=200809L",
       "LOADER_ENABLE_LINUX_SORT",
     ]
   }
+  if (is_fuchsia || is_linux || is_chromeos) {
+    defines += [ "HAVE_ALLOCA_H" ]
+  }
 }
 
 if (!is_android) {
@@ -123,6 +129,7 @@
       "loader/loader_environment.h",
       "loader/loader.c",
       "loader/loader.h",
+      "loader/loader_common.h",
       "loader/log.c",
       "loader/log.h",
       "loader/phys_dev_ext.c",
@@ -161,9 +168,9 @@
       sources += [
         "loader/dirent_on_windows.c",
         "loader/dirent_on_windows.h",
+        "loader/loader.rc",
         "loader/loader_windows.c",
         "loader/loader_windows.h",
-        "loader/loader.rc",
         "loader/vulkan-1.def",
       ]
       if (!is_clang) {
@@ -188,6 +195,7 @@
       libs = [ "Cfgmgr32.lib" ]
     }
     if (is_linux || is_chromeos) {
+      libs = [ "dl" ]
       sources += [
         "loader/loader_linux.c",
         "loader/loader_linux.h",
@@ -196,9 +204,11 @@
     if (is_mac) {
       frameworks = [ "CoreFoundation.framework" ]
     }
+    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
@@ -206,16 +216,27 @@
     if (is_fuchsia) {
       category = "partner"
 
+      symbols_api = "vulkan.ifs"
+
       # 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
 
+      sources += [ "fuchsia/loader_extensions.cc" ]
+
       deps = [
         ":dlopen_fuchsia",
+        "//sdk/fidl/fuchsia.io:fuchsia.io_cpp",
+        "//sdk/fidl/fuchsia.vulkan.loader:fuchsia.vulkan.loader_cpp",
         "//sdk/lib/fdio",
       ]
 
-      runtime_deps = [ "//sdk/lib/fdio:fdio_sdk" ]
+      runtime_deps = [
+        "//sdk/lib/fdio:fdio_sdk",
+        "//zircon/system/ulib/trace-engine:trace-engine_sdk",
+      ]
+
+      libcxx_linkage = "static"
     }
   }
 }
@@ -223,18 +244,32 @@
 if (is_fuchsia) {
   config("fuchsia_config") {
     include_dirs = [ "fuchsia" ]
+
+    if (is_clang || !is_win) {
+      cflags = [
+        "-Wno-newline-eof",
+        "-Wno-strict-prototypes",
+      ]
+    }
   }
 
   source_set("dlopen_fuchsia") {
     public_configs = [ ":fuchsia_config" ]
+    configs += [ ":vulkan_loader_config" ]
 
     sources = [
-      "fuchsia/dlopen_fuchsia.c",
+      "fuchsia/dlopen_fuchsia.cc",
       "fuchsia/dlopen_fuchsia.h",
+      "fuchsia/loader_fuchsia.h",
+      "fuchsia/loader_service.cc",
+      "fuchsia/loader_service.h",
+      "loader/log.h",
     ]
 
     deps = [
-      "//sdk/fidl/fuchsia.vulkan.loader:fuchsia.vulkan.loader_c_client",
+      "$vulkan_headers_dir:vulkan_headers",
+      "//sdk/fidl/fuchsia.vulkan.loader:fuchsia.vulkan.loader_cpp",
+      "//sdk/lib/component/incoming/cpp",
       "//sdk/lib/fdio",
     ]
   }
@@ -251,3 +286,7 @@
     ]
   }
 }
+
+group("tests") {
+  # TODO(fxbug.dev/13288)
+}
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..3db216e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+
+rosasco@google.com
+cstout@google.com
+jbauman@google.com
diff --git a/README.fuchsia b/README.fuchsia
new file mode 100644
index 0000000..06b2a86
--- /dev/null
+++ b/README.fuchsia
@@ -0,0 +1,10 @@
+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/README.md b/fuchsia/README.md
new file mode 100644
index 0000000..a50bd3e
--- /dev/null
+++ b/fuchsia/README.md
@@ -0,0 +1,34 @@
+# Fuchsia extensions to the loader
+
+## Shared library opening
+
+The loader doesn't have direct access to the filesystem containing an ICD. Instead, it must connect
+to the [Vulkan loader service][loader-service] at `/svc/fuchsia.vulkan.loader.Loader`. That service
+can provide an executable VMO for an ICD, which can be loaded using `dlopen_vmo`.
+
+## Open in namespace callback
+
+According to the [Fuchsia system ABI][abi], Vulkan ICDs cannot directly use the filesystem to read
+files or connect to services. Instead loader will call the `vk_icdInitializeOpenInNamespaceCallback`
+function in the ICD immediately after the ICD is dlopened. The callback may be used by the ICD to
+connect to services, or the driver device node or to read from files.
+
+## Exporting device nodes
+
+If an ICD tries to open a path under `/loader-gpu-devices` using
+`vk_icdInitializeOpenInNamespaceCallback`, the loader will redirect that request to a directory
+returned by [fuchsia.vulkan.loader/Loader.ConnectToDeviceFs][connect-device-fs]. This allows the
+loader service to ensure all devnodes the ICD needs are accessible to it, without the application or
+application runtime needing to special-case device nodes in the application's component manifest.
+
+## Getting ICD manifests
+
+[ICD manifests][manifests] are retrieved using [ConnectToManifestFs][connect-manifest-fs] on the
+loader service instead of being read directly from the filesystem. This allows ICD manifests to be
+packaged alongside the ICD shared library. Layer manifests are read from the filesystem like normal.
+
+[loader-service]: https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/graphics/bin/vulkan_loader/README.md
+[abi]: https://fuchsia.dev/fuchsia-src/concepts/system/abi/system?hl=en
+[connect-device-fs]: https://fuchsia.dev/reference/fidl/fuchsia.vulkan.loader?hl=en#fuchsia.vulkan.loader/Loader.ConnectToDeviceFs
+[connect-manifest-fs]: https://fuchsia.dev/reference/fidl/fuchsia.vulkan.loader?hl=en#fuchsia.vulkan.loader/Loader.ConnectToManifestFs
+[manifests]: ../loader/LoaderAndLayerInterface.md
\ No newline at end of file
diff --git a/fuchsia/dlopen_fuchsia.c b/fuchsia/dlopen_fuchsia.c
deleted file mode 100644
index c973b35..0000000
--- a/fuchsia/dlopen_fuchsia.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- *
- * 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);
-    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.cc b/fuchsia/dlopen_fuchsia.cc
new file mode 100644
index 0000000..a8dbea0
--- /dev/null
+++ b/fuchsia/dlopen_fuchsia.cc
@@ -0,0 +1,174 @@
+/*
+ *
+ * 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 <fidl/fuchsia.io/cpp/wire.h>
+#include <lib/fdio/directory.h>
+#include <lib/fdio/io.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <threads.h>
+#include <zircon/dlfcn.h>
+#include <zircon/syscalls.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "loader_service.h"
+
+static char g_error[256] = {};
+
+const char *dlerror_fuchsia(void) { return g_error; }
+
+namespace fio = fuchsia_io;
+
+struct FuchsiaLoaderData {
+    std::mutex table_lock;
+
+    // Keep a cache of ICD shared libraries to avoid loading the same library
+    // multiple times. dlclose doesn't currently do anything on Fuchsia, so we never
+    // bother calling it.
+    std::unordered_map<std::string, void *> icd_dso_map __attribute__((guarded_by(table_lock)));
+    std::unordered_set<void *> icd_dso_set __attribute__((guarded_by(table_lock)));
+};
+
+static FuchsiaLoaderData *fuchsia_loader_data;
+
+void fuchsia_initialize() { fuchsia_loader_data = new FuchsiaLoaderData; }
+
+void fuchsia_teardown() { delete fuchsia_loader_data; }
+
+static void append_to_error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+static void append_to_error(const char *format, ...) {
+    // Size of an individual error message. Multiple can be concatenated.
+    constexpr size_t kErrorSize = 128;
+    char local_error[kErrorSize];
+
+    va_list args;
+    va_start(args, format);
+    vsnprintf(local_error, sizeof(local_error), format, args);
+    va_end(args);
+
+    // Subtract 1 because the NUL-terminator isn't counted.
+    strncat(g_error, local_error, sizeof(g_error) - strlen(g_error) - 1);
+    // Set last byte to 0 in case there wasn't enough room in g_error to fit the entire string.
+    g_error[sizeof(g_error) - 1] = '\0';
+}
+
+// Some VMOs may be loaded from directories outside the package which the shared library loader doesn't
+// have access to. They can only be loaded by opening the file and using dlopen_vmo.
+static void *dlopen_using_vmo(const char *name, int mode) {
+    int fd;
+    zx_status_t status = fdio_open_fd(
+        name, static_cast<uint32_t>(fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kRightExecutable), &fd);
+    if (status != ZX_OK) {
+        append_to_error("libvulkan.so:dlopen_using_vmo: couldn't open \"%s\" as fd, %s\n", name, zx_status_get_string(status));
+        return nullptr;
+    }
+    zx::vmo vmo;
+    status = fdio_get_vmo_exec(fd, vmo.reset_and_get_address());
+    close(fd);
+    if (status != ZX_OK) {
+        append_to_error("libvulkan.so:dlopen_using_vmo: couldn't get vmo \"%s\" as exec: %s\n", name, zx_status_get_string(status));
+        return nullptr;
+    }
+    void *result = dlopen_vmo(vmo.get(), mode);
+    if (!result) {
+        append_to_error("%s", dlerror());
+    }
+    return result;
+}
+
+static void *dlopen_from_vulkan_loader(const char *name, int mode, bool *retry_out) {
+    // Connect to the vulkan loader service to request this library.
+    // If loading fails at these early points, the ICD may just not be available
+    // through the loader service, so retry using plain dlopen.
+    *retry_out = true;
+
+    std::lock_guard<std::mutex> lock(fuchsia_loader_data->table_lock);
+    auto it = fuchsia_loader_data->icd_dso_map.find(name);
+    if (it != fuchsia_loader_data->icd_dso_map.end()) {
+        return it->second;
+    }
+
+    auto &loader_svc = get_vulkan_loader_service();
+    if (!loader_svc.is_valid()) {
+        append_to_error("libvulkan.so:dlopen_fuchsia: no connection to loader svc\n");
+        return nullptr;
+    }
+
+    auto get_result = loader_svc->Get(fidl::StringView::FromExternal(name));
+    if (!get_result.ok()) {
+        append_to_error("libvulkan.so:dlopen_fuchsia: Get() of \"%s\" failed: %s\n", name, get_result.status_string());
+        return nullptr;
+    }
+
+    if (!get_result.value().lib) {
+        append_to_error("libvulkan.so:dlopen_fuchsia: Get() returned invalid vmo\n");
+        return nullptr;
+    }
+    // If we've got a valid VMO matching the name then that VMO is expected to work.
+    *retry_out = false;
+
+    void *result = dlopen_vmo(get_result.value().lib.get(), mode);
+    if (!result) {
+        append_to_error("%s", dlerror());
+    } else {
+        fuchsia_loader_data->icd_dso_map[name] = result;
+        fuchsia_loader_data->icd_dso_set.insert(result);
+    }
+    return result;
+}
+
+void *dlopen_fuchsia(const char *name, int mode, bool driver) {
+    // All errors are appended to g_error, so reset it to empty.
+    g_error[0] = '\0';
+    void *result;
+    if (driver) {
+        // ICDs generally should by loaded from the loader service, so try that first.
+        bool retry{};
+        result = dlopen_from_vulkan_loader(name, mode, &retry);
+        if (result || !retry) {
+            return result;
+        }
+    }
+    result = dlopen(name, mode);
+    if (result != nullptr) {
+        return result;
+    }
+    return dlopen_using_vmo(name, mode);
+}
+
+void dlclose_fuchsia(void *dso) {
+    {
+        std::lock_guard<std::mutex> lock(fuchsia_loader_data->table_lock);
+        if (fuchsia_loader_data->icd_dso_set.count(dso)) {
+            // dlclose doesn't actually do anything on Fuchsia, so it's better
+            // to cache the shared object forever rather than leak it.
+            return;
+        }
+    }
+    // It's fine to dlclose normal shared objects with sonames because the
+    // library loader caches them by soname.
+    dlclose(dso);
+}
diff --git a/fuchsia/dlopen_fuchsia.h b/fuchsia/dlopen_fuchsia.h
index a674b8d..2310ccb 100644
--- a/fuchsia/dlopen_fuchsia.h
+++ b/fuchsia/dlopen_fuchsia.h
@@ -25,5 +25,6 @@
 // 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);
+void dlclose_fuchsia(void *dso);
 
 __END_CDECLS
diff --git a/fuchsia/loader_extensions.cc b/fuchsia/loader_extensions.cc
new file mode 100644
index 0000000..790fd96
--- /dev/null
+++ b/fuchsia/loader_extensions.cc
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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 <fidl/fuchsia.io/cpp/wire.h>
+#include <lib/fdio/directory.h>
+#include <lib/stdcompat/string_view.h>
+#include <limits.h>
+
+#include "loader.h"
+#include "loader_service.h"
+#include "log.h"
+
+namespace {
+
+using PFN_vkOpenInNamespaceAddr = VkResult (*)(const char*, uint32_t);
+using PFN_vkInitializeOpenInNamespaceCallbackAddr = PFN_vkVoidFunction (*)(PFN_vkOpenInNamespaceAddr);
+
+VKAPI_ATTR VkResult VKAPI_CALL loader_fdio_open_in_namespace(const char* const pName, uint32_t handle) {
+    constexpr std::string_view kGpuDevicePrefix = "/loader-gpu-devices";
+    zx_status_t status;
+    if (cpp20::starts_with(std::string_view{pName}, kGpuDevicePrefix)) {
+        // Accesses to subpaths of /loader-gpu-devices should be redirected through the devicefs
+        // exposed by the loader service. See README.md in this directory for details.
+        zx_handle_t device_fs = get_device_fs();
+        if (!device_fs) {
+            return VK_ERROR_INITIALIZATION_FAILED;
+        }
+        const char* new_name = pName + kGpuDevicePrefix.length();
+        if (std::string_view{new_name}.empty()) {
+            new_name = ".";
+        } else {
+            if (new_name[0] != '/') {
+                // Name must be "/loader-gpu-devices/", or that's a bug.
+                return VK_ERROR_INITIALIZATION_FAILED;
+            }
+        }
+        status = fdio_service_connect_at(device_fs, new_name, handle);
+    } else {
+        status = fdio_service_connect(pName, handle);
+    }
+
+    if (status == ZX_OK) {
+        return VK_SUCCESS;
+    }
+    loader_log(nullptr, VK_DEBUG_REPORT_WARNING_BIT_EXT, 0, "Failed to open in namespace %s - error %d", pName, status);
+    return VK_ERROR_INITIALIZATION_FAILED;
+}
+
+}  // namespace
+
+void loader_initialize_icd_services(loader_platform_dl_handle handle) {
+    PFN_vkInitializeOpenInNamespaceCallbackAddr fp_initialize_open_in_namespace_callback_addr;
+
+    fp_initialize_open_in_namespace_callback_addr = reinterpret_cast<PFN_vkInitializeOpenInNamespaceCallbackAddr>(
+        loader_platform_get_proc_address(handle, "vk_icdInitializeOpenInNamespaceCallback"));
+    if (fp_initialize_open_in_namespace_callback_addr) {
+        fp_initialize_open_in_namespace_callback_addr(&loader_fdio_open_in_namespace);
+        // ICD isn't required to expose this entrypoint.
+        return;
+    }
+}
diff --git a/fuchsia/loader_fuchsia.h b/fuchsia/loader_fuchsia.h
new file mode 100644
index 0000000..0d4f7d0
--- /dev/null
+++ b/fuchsia/loader_fuchsia.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright (c) 2021 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>
+
+__BEGIN_CDECLS
+
+// Returns a file descriptor with a directory FD for the loader manifest FS. May
+// return -1 if the loader doesn't support ConnectToManifestFs.
+int get_manifest_fs_fd(void);
+
+// Called on loader shared library construction.
+void fuchsia_initialize();
+// Called on loader shared library destruction.
+void fuchsia_teardown();
+
+__END_CDECLS
diff --git a/fuchsia/loader_service.cc b/fuchsia/loader_service.cc
new file mode 100644
index 0000000..ccdffef
--- /dev/null
+++ b/fuchsia/loader_service.cc
@@ -0,0 +1,134 @@
+/*
+ *
+ * Copyright (c) 2021 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 "loader_service.h"
+
+#include <fcntl.h>
+#include <fidl/fuchsia.io/cpp/wire.h>
+#include <fidl/fuchsia.vulkan.loader/cpp/wire.h>
+#include <lib/component/incoming/cpp/protocol.h>
+#include <lib/fdio/directory.h>
+#include <lib/fdio/fd.h>
+#include <lib/fdio/io.h>
+#include <lib/fidl/cpp/wire/connect_service.h>
+#include <stdio.h>
+#include <string.h>
+#include <threads.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+
+#include "loader.h"
+#include "log.h"
+
+static fidl::WireSyncClient<fuchsia_vulkan_loader::Loader> vulkan_loader_svc;
+
+static zx_handle_t device_fs = ZX_HANDLE_INVALID;
+
+static int manifest_fs_fd = -1;
+
+void connect_to_vulkan_loader_svc() {
+    auto client_end = component::Connect<fuchsia_vulkan_loader::Loader>();
+    if (!client_end.is_ok()) {
+        loader_log(nullptr, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
+                   "connect_to_vulkan_loader_svc: Failed to connect to loader service: %s", client_end.status_string());
+        return;
+    }
+    auto client = fidl::WireSyncClient(std::move(*client_end));
+    auto feature_result = client->GetSupportedFeatures();
+    fuchsia_vulkan_loader::wire::Features features;
+    if (!feature_result.ok()) {
+        loader_log(nullptr, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, 0,
+                   "connect_to_vulkan_loader_svc: Failed to get supported features, error \"%s\".",
+                   feature_result.error().lossy_description());
+        return;
+    }
+    features = feature_result.value().features;
+    constexpr fuchsia_vulkan_loader::wire::Features kMandatoryFeatures =
+        fuchsia_vulkan_loader::wire::Features::kConnectToDeviceFs | fuchsia_vulkan_loader::wire::Features::kConnectToManifestFs |
+        fuchsia_vulkan_loader::wire::Features::kGet;
+    if ((features & kMandatoryFeatures) != kMandatoryFeatures) {
+        loader_log(nullptr, VK_DEBUG_REPORT_INFORMATION_BIT_EXT, 0, "connect_to_vulkan_loader_svc: Missing mandatory feature 0x%x",
+                   kMandatoryFeatures & ~features);
+        return;
+    }
+    zx::channel device_fs_client;
+    {
+        zx::channel device_fs_server;
+        zx_status_t status = zx::channel::create(0, &device_fs_server, &device_fs_client);
+        if (status != ZX_OK) {
+            loader_log(nullptr, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, "connect_to_vulkan_loader_svc: Failed to create channel: %s",
+                       zx_status_get_string(status));
+            return;
+        }
+
+        auto result = client->ConnectToDeviceFs(std::move(device_fs_server));
+        if (!result.ok()) {
+            loader_log(nullptr, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
+                       "connect_to_vulkan_loader_svc: Failed to connect to device fs: %s", result.status_string());
+            return;
+        }
+    }
+    {
+        zx::channel manifest_fs_client;
+        zx::channel manifest_fs_server;
+        zx_status_t status = zx::channel::create(0, &manifest_fs_server, &manifest_fs_client);
+        if (status != ZX_OK) {
+            loader_log(nullptr, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, "connect_to_vulkan_loader_svc: Failed to create channel: %s",
+                       zx_status_get_string(status));
+            return;
+        }
+
+        // Wait for idle so clients will be sure that any existing ICDs will be completely available.
+        auto result = client->ConnectToManifestFs(fuchsia_vulkan_loader::wire::ConnectToManifestOptions::kWaitForIdle,
+                                                  std::move(manifest_fs_server));
+        if (!result.ok()) {
+            loader_log(nullptr, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
+                       "connect_to_vulkan_loader_svc: Failed to connect to manifest fs: %s", result.status_string());
+            return;
+        }
+        status = fdio_fd_create(manifest_fs_client.release(), &manifest_fs_fd);
+        if (status != ZX_OK) {
+            loader_log(nullptr, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
+                       "connect_to_vulkan_loader_svc: Failed to create manifest fs fd: %s", zx_status_get_string(status));
+            return;
+        }
+    }
+
+    vulkan_loader_svc = std::move(client);
+    device_fs = device_fs_client.release();
+}
+
+static once_flag svc_connect_once_flag = ONCE_FLAG_INIT;
+
+fidl::WireSyncClient<fuchsia_vulkan_loader::Loader>& get_vulkan_loader_service() {
+    call_once(&svc_connect_once_flag, connect_to_vulkan_loader_svc);
+
+    return vulkan_loader_svc;
+}
+
+zx_handle_t get_device_fs() {
+    call_once(&svc_connect_once_flag, connect_to_vulkan_loader_svc);
+
+    return device_fs;
+}
+
+int get_manifest_fs_fd(void) {
+    call_once(&svc_connect_once_flag, connect_to_vulkan_loader_svc);
+
+    return manifest_fs_fd;
+}
diff --git a/fuchsia/loader_service.h b/fuchsia/loader_service.h
new file mode 100644
index 0000000..715ffb6
--- /dev/null
+++ b/fuchsia/loader_service.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright (c) 2021 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.
+ *
+ */
+
+#ifndef LOADER_SERVICE_H
+#define LOADER_SERVICE_H
+
+#include <fidl/fuchsia.vulkan.loader/cpp/wire.h>
+#include <zircon/types.h>
+
+#include "loader_fuchsia.h"
+
+// Returns a singleton handle to the fuchsia.vulkan.loader.Loader service. Does not transfer
+// ownership.
+fidl::WireSyncClient<fuchsia_vulkan_loader::Loader>& get_vulkan_loader_service();
+
+// Returns a singleton handle to the device fs retrieved from
+// fuchsia.vulkan.loader/Loader.ConnectToDeviceFs. Does not transfer ownership.
+zx_handle_t get_device_fs();
+
+#endif  // LOADER_SERVICE_H
diff --git a/loader/cJSON.c b/loader/cJSON.c
index 04349fd..256cd94 100644
--- a/loader/cJSON.c
+++ b/loader/cJSON.c
@@ -27,6 +27,7 @@
 /* JSON parser in C. */
 
 #include <ctype.h>
+#include <fcntl.h>
 #include <float.h>
 #include <limits.h>
 #include <math.h>
@@ -933,7 +934,7 @@
     return c;
 }
 
-VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) {
+VkResult loader_get_json(const struct loader_instance *inst, int dir_fd, const char *filename, cJSON **json) {
     FILE *file = NULL;
     char *json_buf = NULL;
     size_t len;
@@ -954,11 +955,27 @@
             }
         }
     }
-#elif COMMON_UNIX_PLATFORMS
-    file = fopen(filename, "rb");
+#else
+#if !defined(__Fuchsia__)
+    assert(dir_fd < 0);
+#endif
+
+    if (dir_fd >= 0) {
+        int file_fd = openat(dir_fd, filename, O_RDONLY);
+        if (file_fd < 0) {
+            loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0, "loader_get_json: Failed to open JSON file \"%s\"", filename);
+            res = VK_ERROR_INITIALIZATION_FAILED;
+            goto out;
+        }
+        file = fdopen(file_fd, "rb");
+    } else {
+#if COMMON_UNIX_PLATFORMS
+        file = fopen(filename, "rb");
 #else
 #warning fopen not available on this platform
 #endif
+    }
+#endif
 
     if (!file) {
         loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename);
diff --git a/loader/cJSON.h b/loader/cJSON.h
index 4878288..0b24894 100644
--- a/loader/cJSON.h
+++ b/loader/cJSON.h
@@ -93,7 +93,7 @@
 //
 // @return -  A pointer to a cJSON object representing the JSON parse tree.
 //            This returned buffer should be freed by caller.
-VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json);
+VkResult loader_get_json(const struct loader_instance *inst, int dir_fd, const char *filename, cJSON **json);
 
 // Given a cJSON object, find the string associated with the key and puts an pre-allocated string into out_string.
 // Length is given by out_str_len, and this function truncates the string with a null terminator if it the provided space isn't
diff --git a/loader/loader.c b/loader/loader.c
index 471a7d8..29eac46 100644
--- a/loader/loader.c
+++ b/loader/loader.c
@@ -28,6 +28,7 @@
 
 #include "loader.h"
 
+#include <fcntl.h>
 #include <ctype.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -69,6 +70,10 @@
 #include "loader_linux.h"
 #endif  // LOADER_ENABLE_LINUX_SORT
 
+#if defined(__Fuchsia__)
+#include "loader_fuchsia.h"
+#endif
+
 // Generated file containing all the extension data
 #include "vk_loader_extensions.c"
 
@@ -1662,6 +1667,11 @@
         goto out;
     }
 
+#if defined(__Fuchsia__)
+    loader_initialize_icd_services(handle);
+#endif
+
+    // Get and settle on an ICD interface version
     // Try to load the driver's exported vk_icdNegotiateLoaderICDInterfaceVersion
     fp_negotiate_icd_version = loader_platform_get_proc_address(handle, "vk_icdNegotiateLoaderICDInterfaceVersion");
 
@@ -1839,6 +1849,9 @@
     windows_initialization();
 #endif
 
+#if defined(__Fuchsia__)
+    fuchsia_initialize();
+#endif
     loader_api_version version = loader_make_full_version(VK_HEADER_VERSION_COMPLETE);
     loader_log(NULL, VULKAN_LOADER_INFO_BIT, 0, "Vulkan Loader Version %d.%d.%d", version.major, version.minor, version.patch);
 
@@ -1864,6 +1877,10 @@
     // Guarantee release of the preloaded ICD libraries. This may have already been called in vkDestroyInstance.
     loader_unload_preloaded_icds();
 
+#if defined(__Fuchsia__)
+    fuchsia_teardown();
+#endif
+
     // release mutexes
     teardown_global_loader_settings();
     loader_platform_thread_delete_mutex(&loader_lock);
@@ -2181,6 +2198,8 @@
         struct loader_layer_properties *props = &instance_layers->list[i];
         if (strcmp(props->info.layerName, VK_OVERRIDE_LAYER_NAME) == 0) {
             if (props->app_key_paths.count > 0) {  // not the global layer
+                                                   // cur_path is unset on Fuchsia, so app_key_path is not supported.
+#if !defined(__Fuchsia__)
                 for (uint32_t j = 0; j < props->app_key_paths.count; j++) {
                     if (strcmp(props->app_key_paths.list[j], cur_path) == 0) {
                         if (!found_active_override_layer) {
@@ -2197,6 +2216,7 @@
                         }
                     }
                 }
+#endif
                 if (!found_active_override_layer) {
                     loader_log(inst, VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_LAYER_BIT, 0,
                                "--Override layer found but not used because app \'%s\' is not in \'app_keys\' list!", cur_path);
@@ -2844,7 +2864,7 @@
 
 // Add any files found in the search_path.  If any path in the search path points to a specific JSON, attempt to
 // only open that one JSON.  Otherwise, if the path is a folder, search the folder for JSON files.
-VkResult add_data_files(const struct loader_instance *inst, char *search_path, struct loader_string_list *out_files,
+VkResult add_data_files(const struct loader_instance *inst, int dir_fd, char *search_path, struct loader_string_list *out_files,
                         bool use_first_found_manifest) {
     VkResult vk_result = VK_SUCCESS;
     DIR *dir_stream = NULL;
@@ -2856,6 +2876,9 @@
 #if !defined(_WIN32)
     char temp_path[2048];
 #endif
+#if !defined(__Fuchsia__)
+    assert(dir_fd < 0);
+#endif
 
     // Now, parse the paths
     next_file = search_path;
@@ -2900,7 +2923,12 @@
                 break;
             }
         } else {  // Otherwise, treat it as a directory
-            dir_stream = loader_opendir(inst, cur_file);
+            if (dir_fd >= 0) {
+                int new_dir_fd = openat(dir_fd, cur_file, O_DIRECTORY);
+                dir_stream = fdopendir(new_dir_fd);
+            } else {
+                dir_stream = loader_opendir(inst, cur_file);
+            }
             if (NULL == dir_stream) {
                 continue;
             }
@@ -2956,6 +2984,7 @@
     char *relative_location = NULL;  // Only used on unix platforms
     size_t rel_size = 0;             // unused in windows, dont declare so no compiler warnings are generated
 #endif
+    int dir_fd = -1;
 
 #if defined(_WIN32)
     char *package_path = NULL;
@@ -3073,6 +3102,15 @@
         override_path = override_env;
     }
 
+#if defined(__Fuchsia__)
+    if (!override_path && manifest_type == LOADER_DATA_FILE_MANIFEST_DRIVER) {
+        dir_fd = get_manifest_fs_fd();
+        if (dir_fd >= 0) {
+            override_path = ".";
+        }
+    }
+#endif
+
     // Add two by default for NULL terminator and one path separator on end (just in case)
     search_path_size = 2;
 
@@ -3261,7 +3299,7 @@
     }
 
     // Now, parse the paths and add any manifest files found in them.
-    vk_result = add_data_files(inst, search_path, out_files, use_first_found_manifest);
+    vk_result = add_data_files(inst, dir_fd, search_path, out_files, use_first_found_manifest);
 
     if (log_flags != 0 && out_files->count > 0) {
         loader_log(inst, log_flags, 0, "   Found the following files:");
@@ -3385,7 +3423,7 @@
 
 // Takes a json file, opens, reads, and parses an ICD Manifest out of it.
 // Should only return VK_SUCCESS, VK_ERROR_INCOMPATIBLE_DRIVER, or VK_ERROR_OUT_OF_HOST_MEMORY
-VkResult loader_parse_icd_manifest(const struct loader_instance *inst, char *file_str, struct ICDManifestInfo *icd,
+VkResult loader_parse_icd_manifest(const struct loader_instance *inst, int dir_fd, char *file_str, struct ICDManifestInfo *icd,
                                    bool *skipped_portability_drivers) {
     VkResult res = VK_SUCCESS;
     cJSON *json = NULL;
@@ -3397,7 +3435,7 @@
         goto out;
     }
 
-    res = loader_get_json(inst, file_str, &json);
+    res = loader_get_json(inst, dir_fd, file_str, &json);
     if (res == VK_ERROR_OUT_OF_HOST_MEMORY) {
         goto out;
     }
@@ -3602,9 +3640,13 @@
     memset(icd_details, 0, sizeof(struct ICDManifestInfo) * manifest_files.count);
 
     for (uint32_t i = 0; i < manifest_files.count; i++) {
+        int dir_fd = -1;
+#if defined(__Fuchsia__)
+        dir_fd = get_manifest_fs_fd();
+#endif
         VkResult icd_res = VK_SUCCESS;
 
-        icd_res = loader_parse_icd_manifest(inst, manifest_files.list[i], &icd_details[i], skipped_portability_drivers);
+        icd_res = loader_parse_icd_manifest(inst, dir_fd, manifest_files.list[i], &icd_details[i], skipped_portability_drivers);
         if (VK_ERROR_OUT_OF_HOST_MEMORY == icd_res) {
             res = icd_res;
             goto out;
@@ -3706,7 +3748,7 @@
 
         // Parse file into JSON struct
         cJSON *json = NULL;
-        VkResult local_res = loader_get_json(inst, file_str, &json);
+        VkResult local_res = loader_get_json(inst, -1, file_str, &json);
         if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) {
             res = VK_ERROR_OUT_OF_HOST_MEMORY;
             goto out;
@@ -5006,7 +5048,8 @@
         prop = loader_find_layer_property(ppEnabledLayerNames[i], list);
         if (NULL == prop) {
             loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
-                       "loader_validate_layers: Layer %d does not exist in the list of available layers", i);
+                       "loader_validate_layers: Layer at index %d (%s) does not exist in the list of available layers", i,
+                       ppEnabledLayerNames[i]);
             return VK_ERROR_LAYER_NOT_PRESENT;
         }
         if (inst->settings.settings_active && prop->settings_control_value != LOADER_SETTINGS_LAYER_CONTROL_ON &&
diff --git a/loader/loader.h b/loader/loader.h
index 93346a9..87cc753 100644
--- a/loader/loader.h
+++ b/loader/loader.h
@@ -32,6 +32,10 @@
 #include "loader_common.h"
 #include "cJSON.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 // Declare the once_init variable
 LOADER_PLATFORM_THREAD_ONCE_EXTERN_DEFINITION(once_init)
 
@@ -201,8 +205,9 @@
                                             VkPhysicalDeviceGroupProperties *groups);
 
 VkStringErrorFlags vk_string_validate(const int max_length, const char *char_array);
+
 char *loader_get_next_path(char *path);
-VkResult add_data_files(const struct loader_instance *inst, char *search_path, struct loader_string_list *out_files,
+VkResult add_data_files(const struct loader_instance *inst, int dir_fd, char *search_path, struct loader_string_list *out_files,
                         bool use_first_found_manifest);
 
 loader_api_version loader_make_version(uint32_t version);
@@ -219,3 +224,11 @@
 #if !defined(LOADER_VERSION_1_1_0)
 #define LOADER_VERSION_1_1_0 loader_combine_version(1, 1, 0)
 #endif
+
+#if defined(__Fuchsia__)
+void loader_initialize_icd_services(loader_platform_dl_handle handle);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/loader/log.h b/loader/log.h
index 58db918..7c7522c 100644
--- a/loader/log.h
+++ b/loader/log.h
@@ -35,6 +35,10 @@
 
 struct loader_instance;
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 enum vulkan_loader_debug_flags {
     VULKAN_LOADER_INFO_BIT = 0x01,
     VULKAN_LOADER_WARN_BIT = 0x02,
@@ -75,3 +79,7 @@
                                            const char *func_name) ASM_NAME("loader_log_asm_function_not_supported");
 
 #undef ASM_NAME
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/loader/settings.c b/loader/settings.c
index c12ebac..f7780c7 100644
--- a/loader/settings.c
+++ b/loader/settings.c
@@ -312,7 +312,7 @@
 #warning "Unsupported platform - must specify platform specific location for vk_loader_settings.json"
 #endif
 
-    res = loader_get_json(inst, settings_file_path, &json);
+    res = loader_get_json(inst, -1, settings_file_path, &json);
     // Make sure sure the top level json value is an object
     if (res != VK_SUCCESS || NULL == json || json->type != 6) {
         goto out;
@@ -567,7 +567,7 @@
         }
 
         cJSON* json = NULL;
-        VkResult local_res = loader_get_json(inst, layer_config->path, &json);
+        VkResult local_res = loader_get_json(inst, -1, layer_config->path, &json);
         if (VK_ERROR_OUT_OF_HOST_MEMORY == local_res) {
             res = VK_ERROR_OUT_OF_HOST_MEMORY;
             goto out;
diff --git a/loader/vk_loader_platform.h b/loader/vk_loader_platform.h
index 3a8cafb..aa8aa6d 100644
--- a/loader/vk_loader_platform.h
+++ b/loader/vk_loader_platform.h
@@ -55,6 +55,8 @@
 #include <pthread.h>
 #include <stdlib.h>
 #include <libgen.h>
+#include <stdio.h>
+#include <unistd.h>
 
 #elif defined(_WIN32)
 // WinBase.h defines CreateSemaphore and synchapi.h defines CreateEvent
@@ -312,7 +314,15 @@
 
     return buffer;
 }
-#elif defined(__Fuchsia__) || defined(__OpenBSD__)
+#elif defined(__Fuchsia__)
+static inline char *loader_platform_executable_path(char *buffer, size_t size) {
+    if (size < 1)
+        return NULL;
+    // Fuchsia doesn't support getting the executable path. The blank path should match no layers.
+    buffer[0] = '\0';
+    return buffer;
+}
+#elif defined(__OpenBSD__)
 static inline char *loader_platform_executable_path(char *buffer, size_t size) { return NULL; }
 #elif defined(__QNX__)
 
@@ -378,7 +388,11 @@
 }
 static inline void loader_platform_close_library(loader_platform_dl_handle library) {
     if (!loader_disable_dynamic_library_unloading) {
+#if defined(__Fuchsia__)
+        dlclose_fuchsia(library);
+#else
         dlclose(library);
+#endif
     } else {
         (void)library;
     }
diff --git a/scripts/loader_extension_generator.py b/scripts/loader_extension_generator.py
index c52ca88..be9d22b 100644
--- a/scripts/loader_extension_generator.py
+++ b/scripts/loader_extension_generator.py
@@ -44,6 +44,7 @@
                  'VK_KHR_swapchain',
                  'VK_KHR_display_swapchain',
                  'VK_KHR_get_display_properties2',
+                 'VK_FUCHSIA_imagepipe_surface',
                  'VK_KHR_get_surface_capabilities2',
                  'VK_QNX_screen_surface',
                  'VK_NN_vi_surface']
diff --git a/vulkan.ifs b/vulkan.ifs
new file mode 100644
index 0000000..7ca217b
--- /dev/null
+++ b/vulkan.ifs
@@ -0,0 +1,250 @@
+--- !ifs-v1
+IfsVersion:      3.0
+SoName:          libvulkan.so
+Symbols:
+  - { Name: vkAcquireNextImage2KHR, Type: Func }
+  - { Name: vkAcquireNextImageKHR, Type: Func }
+  - { Name: vkAllocateCommandBuffers, Type: Func }
+  - { Name: vkAllocateDescriptorSets, Type: Func }
+  - { Name: vkAllocateMemory, Type: Func }
+  - { Name: vkBeginCommandBuffer, Type: Func }
+  - { Name: vkBindBufferMemory, Type: Func }
+  - { Name: vkBindBufferMemory2, Type: Func }
+  - { Name: vkBindImageMemory, Type: Func }
+  - { Name: vkBindImageMemory2, Type: Func }
+  - { Name: vkCmdBeginQuery, Type: Func }
+  - { Name: vkCmdBeginRenderPass, Type: Func }
+  - { Name: vkCmdBeginRenderPass2, Type: Func }
+  - { Name: vkCmdBeginRendering, Type: Func }
+  - { Name: vkCmdBindDescriptorSets, Type: Func }
+  - { Name: vkCmdBindIndexBuffer, Type: Func }
+  - { Name: vkCmdBindPipeline, Type: Func }
+  - { Name: vkCmdBindVertexBuffers, Type: Func }
+  - { Name: vkCmdBindVertexBuffers2, Type: Func }
+  - { Name: vkCmdBlitImage, Type: Func }
+  - { Name: vkCmdBlitImage2, Type: Func }
+  - { Name: vkCmdClearAttachments, Type: Func }
+  - { Name: vkCmdClearColorImage, Type: Func }
+  - { Name: vkCmdClearDepthStencilImage, Type: Func }
+  - { Name: vkCmdCopyBuffer, Type: Func }
+  - { Name: vkCmdCopyBuffer2, Type: Func }
+  - { Name: vkCmdCopyBufferToImage, Type: Func }
+  - { Name: vkCmdCopyBufferToImage2, Type: Func }
+  - { Name: vkCmdCopyImage, Type: Func }
+  - { Name: vkCmdCopyImage2, Type: Func }
+  - { Name: vkCmdCopyImageToBuffer, Type: Func }
+  - { Name: vkCmdCopyImageToBuffer2, Type: Func }
+  - { Name: vkCmdCopyQueryPoolResults, Type: Func }
+  - { Name: vkCmdDispatch, Type: Func }
+  - { Name: vkCmdDispatchBase, Type: Func }
+  - { Name: vkCmdDispatchIndirect, Type: Func }
+  - { Name: vkCmdDraw, Type: Func }
+  - { Name: vkCmdDrawIndexed, Type: Func }
+  - { Name: vkCmdDrawIndexedIndirect, Type: Func }
+  - { Name: vkCmdDrawIndexedIndirectCount, Type: Func }
+  - { Name: vkCmdDrawIndirect, Type: Func }
+  - { Name: vkCmdDrawIndirectCount, Type: Func }
+  - { Name: vkCmdEndQuery, Type: Func }
+  - { Name: vkCmdEndRenderPass, Type: Func }
+  - { Name: vkCmdEndRenderPass2, Type: Func }
+  - { Name: vkCmdEndRendering, Type: Func }
+  - { Name: vkCmdExecuteCommands, Type: Func }
+  - { Name: vkCmdFillBuffer, Type: Func }
+  - { Name: vkCmdNextSubpass, Type: Func }
+  - { Name: vkCmdNextSubpass2, Type: Func }
+  - { Name: vkCmdPipelineBarrier, Type: Func }
+  - { Name: vkCmdPipelineBarrier2, Type: Func }
+  - { Name: vkCmdPushConstants, Type: Func }
+  - { Name: vkCmdResetEvent, Type: Func }
+  - { Name: vkCmdResetEvent2, Type: Func }
+  - { Name: vkCmdResetQueryPool, Type: Func }
+  - { Name: vkCmdResolveImage, Type: Func }
+  - { Name: vkCmdResolveImage2, Type: Func }
+  - { Name: vkCmdSetBlendConstants, Type: Func }
+  - { Name: vkCmdSetCullMode, Type: Func }
+  - { Name: vkCmdSetDepthBias, Type: Func }
+  - { Name: vkCmdSetDepthBiasEnable, Type: Func }
+  - { Name: vkCmdSetDepthBounds, Type: Func }
+  - { Name: vkCmdSetDepthBoundsTestEnable, Type: Func }
+  - { Name: vkCmdSetDepthCompareOp, Type: Func }
+  - { Name: vkCmdSetDepthTestEnable, Type: Func }
+  - { Name: vkCmdSetDepthWriteEnable, Type: Func }
+  - { Name: vkCmdSetDeviceMask, Type: Func }
+  - { Name: vkCmdSetEvent, Type: Func }
+  - { Name: vkCmdSetEvent2, Type: Func }
+  - { Name: vkCmdSetFrontFace, Type: Func }
+  - { Name: vkCmdSetLineWidth, Type: Func }
+  - { Name: vkCmdSetPrimitiveRestartEnable, Type: Func }
+  - { Name: vkCmdSetPrimitiveTopology, Type: Func }
+  - { Name: vkCmdSetRasterizerDiscardEnable, Type: Func }
+  - { Name: vkCmdSetScissor, Type: Func }
+  - { Name: vkCmdSetScissorWithCount, Type: Func }
+  - { Name: vkCmdSetStencilCompareMask, Type: Func }
+  - { Name: vkCmdSetStencilOp, Type: Func }
+  - { Name: vkCmdSetStencilReference, Type: Func }
+  - { Name: vkCmdSetStencilTestEnable, Type: Func }
+  - { Name: vkCmdSetStencilWriteMask, Type: Func }
+  - { Name: vkCmdSetViewport, Type: Func }
+  - { Name: vkCmdSetViewportWithCount, Type: Func }
+  - { Name: vkCmdUpdateBuffer, Type: Func }
+  - { Name: vkCmdWaitEvents, Type: Func }
+  - { Name: vkCmdWaitEvents2, Type: Func }
+  - { Name: vkCmdWriteTimestamp, Type: Func }
+  - { Name: vkCmdWriteTimestamp2, Type: Func }
+  - { Name: vkCreateBuffer, Type: Func }
+  - { Name: vkCreateBufferView, Type: Func }
+  - { Name: vkCreateCommandPool, Type: Func }
+  - { Name: vkCreateComputePipelines, Type: Func }
+  - { Name: vkCreateDescriptorPool, Type: Func }
+  - { Name: vkCreateDescriptorSetLayout, Type: Func }
+  - { Name: vkCreateDescriptorUpdateTemplate, Type: Func }
+  - { Name: vkCreateDevice, Type: Func }
+  - { Name: vkCreateDisplayModeKHR, Type: Func }
+  - { Name: vkCreateDisplayPlaneSurfaceKHR, Type: Func }
+  - { Name: vkCreateEvent, Type: Func }
+  - { Name: vkCreateFence, Type: Func }
+  - { Name: vkCreateFramebuffer, Type: Func }
+  - { Name: vkCreateGraphicsPipelines, Type: Func }
+  - { Name: vkCreateHeadlessSurfaceEXT, Type: Func }
+  - { Name: vkCreateImage, Type: Func }
+  - { Name: vkCreateImagePipeSurfaceFUCHSIA, Type: Func }
+  - { Name: vkCreateImageView, Type: Func }
+  - { Name: vkCreateInstance, Type: Func }
+  - { Name: vkCreatePipelineCache, Type: Func }
+  - { Name: vkCreatePipelineLayout, Type: Func }
+  - { Name: vkCreatePrivateDataSlot, Type: Func }
+  - { Name: vkCreateQueryPool, Type: Func }
+  - { Name: vkCreateRenderPass, Type: Func }
+  - { Name: vkCreateRenderPass2, Type: Func }
+  - { Name: vkCreateSampler, Type: Func }
+  - { Name: vkCreateSamplerYcbcrConversion, Type: Func }
+  - { Name: vkCreateSemaphore, Type: Func }
+  - { Name: vkCreateShaderModule, Type: Func }
+  - { Name: vkCreateSharedSwapchainsKHR, Type: Func }
+  - { Name: vkCreateSwapchainKHR, Type: Func }
+  - { Name: vkDestroyBuffer, Type: Func }
+  - { Name: vkDestroyBufferView, Type: Func }
+  - { Name: vkDestroyCommandPool, Type: Func }
+  - { Name: vkDestroyDescriptorPool, Type: Func }
+  - { Name: vkDestroyDescriptorSetLayout, Type: Func }
+  - { Name: vkDestroyDescriptorUpdateTemplate, Type: Func }
+  - { Name: vkDestroyDevice, Type: Func }
+  - { Name: vkDestroyEvent, Type: Func }
+  - { Name: vkDestroyFence, Type: Func }
+  - { Name: vkDestroyFramebuffer, Type: Func }
+  - { Name: vkDestroyImage, Type: Func }
+  - { Name: vkDestroyImageView, Type: Func }
+  - { Name: vkDestroyInstance, Type: Func }
+  - { Name: vkDestroyPipeline, Type: Func }
+  - { Name: vkDestroyPipelineCache, Type: Func }
+  - { Name: vkDestroyPipelineLayout, Type: Func }
+  - { Name: vkDestroyPrivateDataSlot, Type: Func }
+  - { Name: vkDestroyQueryPool, Type: Func }
+  - { Name: vkDestroyRenderPass, Type: Func }
+  - { Name: vkDestroySampler, Type: Func }
+  - { Name: vkDestroySamplerYcbcrConversion, Type: Func }
+  - { Name: vkDestroySemaphore, Type: Func }
+  - { Name: vkDestroyShaderModule, Type: Func }
+  - { Name: vkDestroySurfaceKHR, Type: Func }
+  - { Name: vkDestroySwapchainKHR, Type: Func }
+  - { Name: vkDeviceWaitIdle, Type: Func }
+  - { Name: vkEndCommandBuffer, Type: Func }
+  - { Name: vkEnumerateDeviceExtensionProperties, Type: Func }
+  - { Name: vkEnumerateDeviceLayerProperties, Type: Func }
+  - { Name: vkEnumerateInstanceExtensionProperties, Type: Func }
+  - { Name: vkEnumerateInstanceLayerProperties, Type: Func }
+  - { Name: vkEnumerateInstanceVersion, Type: Func }
+  - { Name: vkEnumeratePhysicalDeviceGroups, Type: Func }
+  - { Name: vkEnumeratePhysicalDevices, Type: Func }
+  - { Name: vkFlushMappedMemoryRanges, Type: Func }
+  - { Name: vkFreeCommandBuffers, Type: Func }
+  - { Name: vkFreeDescriptorSets, Type: Func }
+  - { Name: vkFreeMemory, Type: Func }
+  - { Name: vkGetBufferDeviceAddress, Type: Func }
+  - { Name: vkGetBufferMemoryRequirements, Type: Func }
+  - { Name: vkGetBufferMemoryRequirements2, Type: Func }
+  - { Name: vkGetBufferOpaqueCaptureAddress, Type: Func }
+  - { Name: vkGetDescriptorSetLayoutSupport, Type: Func }
+  - { Name: vkGetDeviceBufferMemoryRequirements, Type: Func }
+  - { Name: vkGetDeviceGroupPeerMemoryFeatures, Type: Func }
+  - { Name: vkGetDeviceGroupPresentCapabilitiesKHR, Type: Func }
+  - { Name: vkGetDeviceGroupSurfacePresentModesKHR, Type: Func }
+  - { Name: vkGetDeviceImageMemoryRequirements, Type: Func }
+  - { Name: vkGetDeviceImageSparseMemoryRequirements, Type: Func }
+  - { Name: vkGetDeviceMemoryCommitment, Type: Func }
+  - { Name: vkGetDeviceMemoryOpaqueCaptureAddress, Type: Func }
+  - { Name: vkGetDeviceProcAddr, Type: Func }
+  - { Name: vkGetDeviceQueue, Type: Func }
+  - { Name: vkGetDeviceQueue2, Type: Func }
+  - { Name: vkGetDisplayModeProperties2KHR, Type: Func }
+  - { Name: vkGetDisplayModePropertiesKHR, Type: Func }
+  - { Name: vkGetDisplayPlaneCapabilities2KHR, Type: Func }
+  - { Name: vkGetDisplayPlaneCapabilitiesKHR, Type: Func }
+  - { Name: vkGetDisplayPlaneSupportedDisplaysKHR, Type: Func }
+  - { Name: vkGetEventStatus, Type: Func }
+  - { Name: vkGetFenceStatus, Type: Func }
+  - { Name: vkGetImageMemoryRequirements, Type: Func }
+  - { Name: vkGetImageMemoryRequirements2, Type: Func }
+  - { Name: vkGetImageSparseMemoryRequirements, Type: Func }
+  - { Name: vkGetImageSparseMemoryRequirements2, Type: Func }
+  - { Name: vkGetImageSubresourceLayout, Type: Func }
+  - { Name: vkGetInstanceProcAddr, Type: Func }
+  - { Name: vkGetPhysicalDeviceDisplayPlaneProperties2KHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceDisplayPlanePropertiesKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceDisplayProperties2KHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceDisplayPropertiesKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceExternalBufferProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceExternalFenceProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceExternalSemaphoreProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceFeatures, Type: Func }
+  - { Name: vkGetPhysicalDeviceFeatures2, Type: Func }
+  - { Name: vkGetPhysicalDeviceFormatProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceFormatProperties2, Type: Func }
+  - { Name: vkGetPhysicalDeviceImageFormatProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceImageFormatProperties2, Type: Func }
+  - { Name: vkGetPhysicalDeviceMemoryProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceMemoryProperties2, Type: Func }
+  - { Name: vkGetPhysicalDevicePresentRectanglesKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceProperties2, Type: Func }
+  - { Name: vkGetPhysicalDeviceQueueFamilyProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceQueueFamilyProperties2, Type: Func }
+  - { Name: vkGetPhysicalDeviceSparseImageFormatProperties, Type: Func }
+  - { Name: vkGetPhysicalDeviceSparseImageFormatProperties2, Type: Func }
+  - { Name: vkGetPhysicalDeviceSurfaceCapabilities2KHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceSurfaceCapabilitiesKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceSurfaceFormats2KHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceSurfaceFormatsKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceSurfacePresentModesKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceSurfaceSupportKHR, Type: Func }
+  - { Name: vkGetPhysicalDeviceToolProperties, Type: Func }
+  - { Name: vkGetPipelineCacheData, Type: Func }
+  - { Name: vkGetPrivateData, Type: Func }
+  - { Name: vkGetQueryPoolResults, Type: Func }
+  - { Name: vkGetRenderAreaGranularity, Type: Func }
+  - { Name: vkGetSemaphoreCounterValue, Type: Func }
+  - { Name: vkGetSwapchainImagesKHR, Type: Func }
+  - { Name: vkInvalidateMappedMemoryRanges, Type: Func }
+  - { Name: vkMapMemory, Type: Func }
+  - { Name: vkMergePipelineCaches, Type: Func }
+  - { Name: vkQueueBindSparse, Type: Func }
+  - { Name: vkQueuePresentKHR, Type: Func }
+  - { Name: vkQueueSubmit, Type: Func }
+  - { Name: vkQueueSubmit2, Type: Func }
+  - { Name: vkQueueWaitIdle, Type: Func }
+  - { Name: vkResetCommandBuffer, Type: Func }
+  - { Name: vkResetCommandPool, Type: Func }
+  - { Name: vkResetDescriptorPool, Type: Func }
+  - { Name: vkResetEvent, Type: Func }
+  - { Name: vkResetFences, Type: Func }
+  - { Name: vkResetQueryPool, Type: Func }
+  - { Name: vkSetEvent, Type: Func }
+  - { Name: vkSetPrivateData, Type: Func }
+  - { Name: vkSignalSemaphore, Type: Func }
+  - { Name: vkTrimCommandPool, Type: Func }
+  - { Name: vkUnmapMemory, Type: Func }
+  - { Name: vkUpdateDescriptorSetWithTemplate, Type: Func }
+  - { Name: vkUpdateDescriptorSets, Type: Func }
+  - { Name: vkWaitForFences, Type: Func }
+  - { Name: vkWaitSemaphores, Type: Func }
+...