vulkan: rewrite top of loader

The top is responsible for layer discovery and chaining, and the bottom is
like a regular layer that is always enabled and is inserted just before
the driver.  Make the separation clear by rewriting the top and stripping
the layer managment code from loader.cpp.

Change-Id: I64e525e27bd4c297bccd94a1eb9b88e28088e85d
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
new file mode 100644
index 0000000..7ebe983
--- /dev/null
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -0,0 +1,519 @@
+{{define "Copyright"}}
+/*
+•* Copyright 2016 The Android Open Source Project
+•*
+•* 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.
+•*/
+¶{{end}}
+
+{{Include "../api/templates/vulkan_common.tmpl"}}
+{{Global "clang-format" (Strings "clang-format" "-style=file")}}
+{{Macro "DefineGlobals" $}}
+{{$ | Macro "api_gen.h"   | Format (Global "clang-format") | Write "api_gen.h"  }}
+{{$ | Macro "api_gen.cpp" | Format (Global "clang-format") | Write "api_gen.cpp"}}
+
+{{/*
+-------------------------------------------------------------------------------
+  api_gen.h
+-------------------------------------------------------------------------------
+*/}}
+{{define "api_gen.h"}}
+{{Macro "Copyright"}}

+// WARNING: This file is generated. See ../README.md for instructions.

+#ifndef LIBVULKAN_API_GEN_H
+#define LIBVULKAN_API_GEN_H

+#include <vulkan/vulkan.h>

+namespace vulkan {«
+namespace api {«

+struct InstanceDispatchTable {
+  // clang-format off
+  {{range $f := AllCommands $}}
+    {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}}
+      {{Macro "C++.DeclareDispatchTableEntry" $f}};
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+struct DeviceDispatchTable {
+  // clang-format off
+  {{range $f := AllCommands $}}
+    {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}}
+      {{Macro "C++.DeclareDispatchTableEntry" $f}};
+    {{end}}
+  {{end}}
+  // clang-format on
+};

+bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc);
+bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc);

+»} // namespace api
+»} // namespace vulkan

+#endif // LIBVULKAN_API_GEN_H
+¶{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  api_gen.cpp
+-------------------------------------------------------------------------------
+*/}}
+{{define "api_gen.cpp"}}
+{{Macro "Copyright"}}

+// WARNING: This file is generated. See ../README.md for instructions.

+#include <string.h>
+#include <algorithm>
+#include <log/log.h>

+#include "api.h"

+namespace vulkan {«
+namespace api {«

+{{Macro "C++.DefineInitProcMacros" "dispatch"}}

+bool InitDispatchTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc) {
+    auto& data = GetData(instance);
+    bool success = true;
+    ¶
+    // clang-format off
+    {{range $f := AllCommands $}}
+      {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}}
+        {{Macro "C++.InitProc" $f}}
+      {{end}}
+    {{end}}
+    // clang-format on
+    ¶
+    return success;
+}

+bool InitDispatchTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc) {
+    auto& data = GetData(dev);
+    bool success = true;
+    ¶
+    // clang-format off
+    {{range $f := AllCommands $}}
+      {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}}
+        {{Macro "C++.InitProc" $f}}
+      {{end}}
+    {{end}}
+    // clang-format on
+    ¶
+    return success;
+}

+»} // namespace api
+»} // namespace vulkan

+// clang-format off

+{{range $f := AllCommands $}}
+  {{if (Macro "IsFunctionExported" $f)}}
+    __attribute__((visibility("default")))
+    VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) {
+      {{     if eq $f.Name "vkGetInstanceProcAddr"}}
+        {{Macro "api.C++.InterceptInstanceProcAddr" $}}
+      {{else if eq $f.Name "vkGetDeviceProcAddr"}}
+        {{Macro "api.C++.InterceptDeviceProcAddr" $}}
+      {{end}}
+
+      {{Macro "api.C++.Dispatch" $f}}
+    }
+    ¶
+  {{end}}
+{{end}}

+// clang-format on
+¶{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits a declaration of a dispatch table entry.
+------------------------------------------------------------------------------
+*/}}
+{{define "C++.DeclareDispatchTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{Macro "FunctionPtrName" $}} {{Macro "BaseName" $}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits macros to help initialize dispatch tables.
+-------------------------------------------------------------------------------
+*/}}
+{{define "C++.DefineInitProcMacros"}}
+  #define UNLIKELY(expr) __builtin_expect((expr), 0)
+  ¶
+  #define INIT_PROC(obj, proc) do {                             \
+      data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>(       \
+              get_proc(obj, "vk" # proc));                      \
+      if (UNLIKELY(!data.{{$}}.proc)) {                         \
+          ALOGE("missing " # obj " proc: vk" # proc);           \
+          success = false;                                      \
+      }                                                         \
+  } while(0)
+  ¶
+  // TODO do we want to point to a stub or nullptr when ext is not enabled?
+  #define INIT_PROC_EXT(ext, obj, proc) do {                    \
+      INIT_PROC(obj, proc);                                     \
+  } while(0)
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits code to invoke INIT_PROC or INIT_PROC_EXT.
+-------------------------------------------------------------------------------
+*/}}
+{{define "C++.InitProc"}}
+  {{AssertType $ "Function"}}
+
+  {{$ext := GetAnnotation $ "extension"}}
+  {{if $ext}}
+    INIT_PROC_EXT({{Macro "BaseName" $ext}}, §
+  {{else}}
+    INIT_PROC(§
+  {{end}}
+
+  {{if (Macro "IsInstanceDispatched" $)}}
+    instance, §
+  {{else}}
+    dev, §
+  {{end}}
+
+  {{Macro "BaseName" $}});
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is exported and instance-dispatched.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.IsInstanceDispatchTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is exported and device-dispatched.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.IsDeviceDispatchTableEntry"}}
+  {{AssertType $ "Function"}}
+
+  {{if and (Macro "IsFunctionExported" $) (Macro "IsDeviceDispatched" $)}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits true if a function is intercepted by vulkan::api.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.IsIntercepted"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "IsFunctionSupported" $)}}
+    {{/* Global functions cannot be dispatched at all */}}
+    {{     if (Macro "IsGloballyDispatched" $)}}true
+
+    {{/* VkPhysicalDevice functions that manage device layers */}}
+    {{else if eq $.Name "vkCreateDevice"}}true
+    {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
+    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
+
+    {{/* Destroy functions of dispatchable objects */}}
+    {{else if eq $.Name "vkDestroyInstance"}}true
+    {{else if eq $.Name "vkDestroyDevice"}}true
+
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits code for vkGetInstanceProcAddr for function interception.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.InterceptInstanceProcAddr"}}
+  {{AssertType $ "API"}}
+
+  // global functions
+  if (!instance) {
+    {{range $f := AllCommands $}}
+      {{if (Macro "IsGloballyDispatched" $f)}}
+        if (strcmp(pName, "{{$f.Name}}") == 0) return §
+          reinterpret_cast<PFN_vkVoidFunction>(§
+            vulkan::api::{{Macro "BaseName" $f}});
+      {{end}}
+    {{end}}
+    ¶
+    ALOGE("vkGetInstanceProcAddr called with %s without instance",  pName);
+    return nullptr;
+  }
+  ¶
+  static const struct Hook {
+    const char* name;
+    PFN_vkVoidFunction proc;
+  } hooks[] = {
+    {{range $f := SortBy (AllCommands $) "FunctionName"}}
+      {{if (Macro "IsFunctionExported" $f)}}
+        {{/* hide global functions */}}
+        {{if (Macro "IsGloballyDispatched" $f)}}
+          { "{{$f.Name}}", nullptr },
+
+        {{/* redirect intercepted functions */}}
+        {{else if (Macro "api.IsIntercepted" $f)}}
+          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
+            vulkan::api::{{Macro "BaseName" $f}}) },
+
+        {{/* redirect vkGetInstanceProcAddr to itself */}}
+        {{else if eq $f.Name "vkGetInstanceProcAddr"}}
+          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}}) },
+
+        {{/* redirect device functions to themselves as a workaround for
+             layers that do not intercept in their vkGetInstanceProcAddr */}}
+        {{else if (Macro "IsDeviceDispatched" $f)}}
+          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{$f.Name}}) },
+
+        {{end}}
+      {{end}}
+    {{end}}
+  };
+  // clang-format on
+  constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
+  auto hook = std::lower_bound(
+    hooks, hooks + count, pName,
+    [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
+  if (hook <  hooks + count && strcmp(hook->name, pName) == 0) {
+    if (!hook->proc)
+      ALOGE("vkGetInstanceProcAddr called with %s with instance",  pName);
+    return hook->proc;
+  }
+  // clang-format off
+  ¶
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits code for vkGetDeviceProcAddr for function interception.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.InterceptDeviceProcAddr"}}
+  {{AssertType $ "API"}}
+
+  if (device == VK_NULL_HANDLE) {
+    ALOGE("vkGetDeviceProcAddr called with invalid device");
+    return nullptr;
+  }
+  ¶
+  static const char* const known_non_device_names[] = {
+    {{range $f := SortBy (AllCommands $) "FunctionName"}}
+      {{if (Macro "IsFunctionSupported" $f)}}
+        {{if not (Macro "IsDeviceDispatched" $f)}}
+          "{{$f.Name}}",
+        {{end}}
+      {{end}}
+    {{end}}
+  };
+  // clang-format on
+  constexpr size_t count = sizeof(known_non_device_names) /
+    sizeof(known_non_device_names[0]);
+  if (!pName ||
+      std::binary_search(
+        known_non_device_names, known_non_device_names + count, pName,
+        [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
+    ALOGE("vkGetDeviceProcAddr called with %s", pName);
+    return nullptr;
+  }
+  // clang-format off
+  ¶
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emits code to dispatch a function.
+------------------------------------------------------------------------------
+*/}}
+{{define "api.C++.Dispatch"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "api.IsIntercepted" $)}}// call into api.cpp{{end}}
+  {{if not (IsVoid $.Return.Type)}}return §{{end}}
+
+  {{if (Macro "api.IsIntercepted" $)}}
+    vulkan::api::§
+  {{else}}
+    {{$p0 := index $.CallParameters 0}}
+    vulkan::api::GetData({{$p0.Name}}).dispatch.§
+  {{end}}
+
+  {{Macro "BaseName" $}}({{Macro "Arguments" $}});
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits a function/extension name without the "vk"/"VK_" prefix.
+-------------------------------------------------------------------------------
+*/}}
+{{define "BaseName"}}
+  {{     if IsFunction $}}{{TrimPrefix "vk" $.Name}}
+  {{else if eq $.Name "extension"}}{{TrimPrefix "VK_" (index $.Arguments 0)}}
+  {{else}}{{Error "invalid use of BaseName"}}
+  {{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  Emits a comma-separated list of C parameter names for the given command.
+-------------------------------------------------------------------------------
+*/}}
+{{define "Arguments"}}
+  {{AssertType $ "Function"}}
+
+  {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+*/}}
+{{define "IsGloballyDispatched"}}
+  {{AssertType $ "Function"}}
+  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Global")}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" for supported functions that undergo table dispatch. Only global
+  functions and functions handled in the loader top without calling into
+  lower layers are not dispatched.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsInstanceDispatched"}}
+  {{AssertType $ "Function"}}
+  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" for supported functions that can have device-specific dispatch.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsDeviceDispatched"}}
+  {{AssertType $ "Function"}}
+  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Device")}}
+    true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" if a function is core or from a supportable extension.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsFunctionSupported"}}
+  {{AssertType $ "Function"}}
+  {{if not (GetAnnotation $ "pfn")}}
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if not $ext}}true
+    {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Decides whether a function should be exported from the Android Vulkan
+  library. Functions in the core API and in loader extensions are exported.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsFunctionExported"}}
+  {{AssertType $ "Function"}}
+
+  {{if (Macro "IsFunctionSupported" $)}}
+    {{$ext := GetAnnotation $ "extension"}}
+    {{if $ext}}
+      {{Macro "IsExtensionExported" $ext}}
+    {{else}}
+      true
+    {{end}}
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Emit "true" if an extension is unsupportable on Android.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsExtensionBlacklisted"}}
+  {{$ext := index $.Arguments 0}}
+  {{     if eq $ext "VK_KHR_display"}}true
+  {{else if eq $ext "VK_KHR_display_swapchain"}}true
+  {{else if eq $ext "VK_KHR_xlib_surface"}}true
+  {{else if eq $ext "VK_KHR_xcb_surface"}}true
+  {{else if eq $ext "VK_KHR_wayland_surface"}}true
+  {{else if eq $ext "VK_KHR_mir_surface"}}true
+  {{else if eq $ext "VK_KHR_win32_surface"}}true
+  {{end}}
+{{end}}
+
+
+{{/*
+------------------------------------------------------------------------------
+  Reports whether an extension is implemented entirely by the loader,
+  so drivers should not enumerate it.
+------------------------------------------------------------------------------
+*/}}
+{{define "IsExtensionExported"}}
+  {{$ext := index $.Arguments 0}}
+  {{     if eq $ext "VK_KHR_surface"}}true
+  {{else if eq $ext "VK_KHR_swapchain"}}true
+  {{else if eq $ext "VK_KHR_android_surface"}}true
+  {{end}}
+{{end}}